markdown-exit-s3-image 2.0.0 → 3.0.0
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.
- package/README.md +60 -0
- package/dist/index.d.mts +21 -2
- package/dist/index.mjs +107 -69
- package/dist/types.d.mts +61 -1
- package/package.json +6 -5
- package/dist/types-DK7PR0JE.d.mts +0 -62
package/README.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# markdown-exit-s3-image
|
|
2
|
+
|
|
3
|
+
A [markdown-exit](https://github.com/Efterklang/markdown-exit) plugin for S3 images with Bitiful CDN integration.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Progressive image loading** - Blurhash placeholders while images load
|
|
8
|
+
- **Obsidian-style sizing** - `![alt|width]` or `![alt|widthxheight]` syntax
|
|
9
|
+
- **Automatic srcset** - Responsive images with configurable widths
|
|
10
|
+
- **Smart caching** - JSON-based metadata caching
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install markdown-exit-s3-image
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
import { MarkdownExit } from "markdown-exit";
|
|
22
|
+
import { image } from "markdown-exit-s3-image";
|
|
23
|
+
|
|
24
|
+
const md = new MarkdownExit({ html: true });
|
|
25
|
+
md.use(image, {
|
|
26
|
+
bitiful_domains: ["demo.bitiful.com"],
|
|
27
|
+
progressive: { enable: true },
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const html = await md.renderAsync(
|
|
31
|
+
"",
|
|
32
|
+
);
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Options
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
interface Options {
|
|
39
|
+
bitiful_domains: string[]; // Bitiful CDN domains
|
|
40
|
+
ignore_formats?: string[]; // Formats to skip (e.g., ["svg"])
|
|
41
|
+
progressive: {
|
|
42
|
+
enable: boolean; // Enable progressive loading
|
|
43
|
+
srcset_widths?: number[]; // Widths for srcset generation
|
|
44
|
+
};
|
|
45
|
+
cache_path?: string; // Path to cache file
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Obsidian Sizing
|
|
50
|
+
|
|
51
|
+
```markdown
|
|
52
|
+
![alt|300] <!-- width only -->
|
|
53
|
+
![alt|640x480] <!-- width x height -->
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## License
|
|
57
|
+
|
|
58
|
+
Credit: [Barbapapazes/markdown-exit-image: Erase images CLS automatically with this Markdown Exit plugin.](https://github.com/Barbapapazes/markdown-exit-image)
|
|
59
|
+
|
|
60
|
+
MIT
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,26 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Options } from "./types.mjs";
|
|
2
2
|
import { MarkdownExit } from "markdown-exit";
|
|
3
3
|
|
|
4
4
|
//#region src/index.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Parsed result from Obsidian-style image alt text.
|
|
7
|
+
*/
|
|
8
|
+
interface ParsedImageAlt {
|
|
9
|
+
alt: string;
|
|
10
|
+
width?: number;
|
|
11
|
+
height?: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Parse Obsidian-style image dimensions from alt text.
|
|
15
|
+
* Supports formats: `![alt|width]`, `![alt|widthxheight]`
|
|
16
|
+
* Examples:
|
|
17
|
+
* - `![image|300]` -> width: 300
|
|
18
|
+
* - `![image|300x200]` -> width: 300, height: 200
|
|
19
|
+
*
|
|
20
|
+
* @param content The image alt text content
|
|
21
|
+
* @returns Parsed result with alt text and optional dimensions
|
|
22
|
+
*/
|
|
23
|
+
declare function parseObsidianImageAlt(content: string): ParsedImageAlt;
|
|
5
24
|
declare function image(md: MarkdownExit, userOptions: Options): void;
|
|
6
25
|
//#endregion
|
|
7
|
-
export { image };
|
|
26
|
+
export { image, parseObsidianImageAlt };
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,59 @@
|
|
|
1
|
-
import { promises } from "node:fs";
|
|
2
|
-
import { thumbHashToDataURL } from "thumbhash";
|
|
3
1
|
import sharp from "sharp";
|
|
2
|
+
import { thumbHashToDataURL } from "thumbhash";
|
|
3
|
+
import { promises } from "node:fs";
|
|
4
4
|
|
|
5
|
+
//#region src/bitiful.ts
|
|
6
|
+
/**
|
|
7
|
+
* Bitiful CDN integration utilities
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Check if URL belongs to Bitiful CDN.
|
|
11
|
+
*/
|
|
12
|
+
function isBitifulDomain(url, bitifulDomains) {
|
|
13
|
+
try {
|
|
14
|
+
const hostname = new URL(url).hostname;
|
|
15
|
+
return bitifulDomains.some((domain) => hostname.includes(domain));
|
|
16
|
+
} catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Fetch dimension info from Bitiful API.
|
|
22
|
+
*/
|
|
23
|
+
async function getBitifulDimension(imageUrl) {
|
|
24
|
+
try {
|
|
25
|
+
const baseUrl = imageUrl.split("?")[0];
|
|
26
|
+
const response = await fetch(`${baseUrl}?fmt=info`);
|
|
27
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
28
|
+
const { ImageWidth: width, ImageHeight: height } = await response.json();
|
|
29
|
+
if (typeof width === "number" && typeof height === "number") return {
|
|
30
|
+
width,
|
|
31
|
+
height
|
|
32
|
+
};
|
|
33
|
+
return null;
|
|
34
|
+
} catch (error) {
|
|
35
|
+
console.warn(`[Bitiful] Dimension error for ${imageUrl}:`, error instanceof Error ? error.message : String(error));
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Fetch thumbhash data URL from Bitiful API.
|
|
41
|
+
*/
|
|
42
|
+
async function getBitifulThumbhash(imageUrl) {
|
|
43
|
+
try {
|
|
44
|
+
const baseUrl = imageUrl.split("?")[0];
|
|
45
|
+
const response = await fetch(`${baseUrl}?fmt=thumbhash`);
|
|
46
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
47
|
+
const base64String = await response.text();
|
|
48
|
+
const base64Data = thumbHashToDataURL(new Uint8Array(Buffer.from(base64String.trim(), "base64"))).replace(/^data:image\/\w+;base64,/, "");
|
|
49
|
+
return `data:image/webp;base64,${(await sharp(Buffer.from(base64Data, "base64")).webp({ quality: 80 }).toBuffer()).toString("base64")}`;
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.warn(`[Bitiful] Thumbhash error for ${imageUrl}:`, error instanceof Error ? error.message : String(error));
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
//#endregion
|
|
5
57
|
//#region src/cache.ts
|
|
6
58
|
var ImageCache = class {
|
|
7
59
|
cache = {};
|
|
@@ -109,59 +161,26 @@ function resolveOptions(userOptions = {}) {
|
|
|
109
161
|
}
|
|
110
162
|
|
|
111
163
|
//#endregion
|
|
112
|
-
//#region src/
|
|
113
|
-
/**
|
|
114
|
-
* Bitiful CDN integration utilities
|
|
115
|
-
*/
|
|
116
|
-
/**
|
|
117
|
-
* Check if URL belongs to Bitiful CDN.
|
|
118
|
-
*/
|
|
119
|
-
function isBitifulDomain(url, bitifulDomains) {
|
|
120
|
-
try {
|
|
121
|
-
const hostname = new URL(url).hostname;
|
|
122
|
-
return bitifulDomains.some((domain) => hostname.includes(domain));
|
|
123
|
-
} catch {
|
|
124
|
-
return false;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* Fetch dimension info from Bitiful API.
|
|
129
|
-
*/
|
|
130
|
-
async function getBitifulDimension(imageUrl) {
|
|
131
|
-
try {
|
|
132
|
-
const baseUrl = imageUrl.split("?")[0];
|
|
133
|
-
const response = await fetch(`${baseUrl}?fmt=info`);
|
|
134
|
-
if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
135
|
-
const { ImageWidth: width, ImageHeight: height } = await response.json();
|
|
136
|
-
if (typeof width === "number" && typeof height === "number") return {
|
|
137
|
-
width,
|
|
138
|
-
height
|
|
139
|
-
};
|
|
140
|
-
return null;
|
|
141
|
-
} catch (error) {
|
|
142
|
-
console.warn(`[Bitiful] Dimension error for ${imageUrl}:`, error instanceof Error ? error.message : String(error));
|
|
143
|
-
return null;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
164
|
+
//#region src/index.ts
|
|
146
165
|
/**
|
|
147
|
-
*
|
|
166
|
+
* Parse Obsidian-style image dimensions from alt text.
|
|
167
|
+
* Supports formats: `![alt|width]`, `![alt|widthxheight]`
|
|
168
|
+
* Examples:
|
|
169
|
+
* - `![image|300]` -> width: 300
|
|
170
|
+
* - `![image|300x200]` -> width: 300, height: 200
|
|
171
|
+
*
|
|
172
|
+
* @param content The image alt text content
|
|
173
|
+
* @returns Parsed result with alt text and optional dimensions
|
|
148
174
|
*/
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
} catch (error) {
|
|
158
|
-
console.warn(`[Bitiful] Thumbhash error for ${imageUrl}:`, error instanceof Error ? error.message : String(error));
|
|
159
|
-
return null;
|
|
160
|
-
}
|
|
175
|
+
function parseObsidianImageAlt(content) {
|
|
176
|
+
const match = content.trim().match(/^(.*?)\|(\d+)(?:x(\d+))?$/);
|
|
177
|
+
if (!match) return { alt: content };
|
|
178
|
+
return {
|
|
179
|
+
alt: match[1].trim(),
|
|
180
|
+
width: parseInt(match[2], 10),
|
|
181
|
+
height: match[3] ? parseInt(match[3], 10) : void 0
|
|
182
|
+
};
|
|
161
183
|
}
|
|
162
|
-
|
|
163
|
-
//#endregion
|
|
164
|
-
//#region src/index.ts
|
|
165
184
|
/**
|
|
166
185
|
* Check if file format should be ignored based on URL extension.
|
|
167
186
|
*/
|
|
@@ -171,24 +190,29 @@ function shouldIgnoreFormat(url, formats) {
|
|
|
171
190
|
}
|
|
172
191
|
function image(md, userOptions) {
|
|
173
192
|
const options = resolveOptions(userOptions);
|
|
174
|
-
if (!options.progressive.enable) return;
|
|
175
193
|
const cachePath = options.cache_path;
|
|
176
|
-
const cache = cachePath ? new ImageCache(cachePath) : null;
|
|
177
|
-
if (cache)
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
194
|
+
const cache = cachePath && options.progressive.enable ? new ImageCache(cachePath) : null;
|
|
195
|
+
if (cache) {
|
|
196
|
+
cache.load().catch((err) => {
|
|
197
|
+
console.error("[ImageCache] Failed to load cache:", err);
|
|
198
|
+
});
|
|
199
|
+
process.once("beforeExit", () => {
|
|
200
|
+
cache.save();
|
|
201
|
+
});
|
|
202
|
+
}
|
|
183
203
|
const imageRule = md.renderer.rules.image;
|
|
184
204
|
if (!imageRule) return;
|
|
185
205
|
md.renderer.rules.image = async (tokens, idx, info, env, self) => {
|
|
186
206
|
const token = tokens[idx];
|
|
187
207
|
const src = token.attrGet("src");
|
|
188
|
-
|
|
208
|
+
const parsedAlt = parseObsidianImageAlt(token.content);
|
|
209
|
+
if (!(options.progressive.enable && src && src.startsWith("http") && isBitifulDomain(src, options.bitiful_domains) && !shouldIgnoreFormat(src, options.ignore_formats) && process.env.NODE_ENV !== "development")) {
|
|
210
|
+
if (parsedAlt.width) return applyDimensionToHTML(await imageRule(tokens, idx, info, env, self), parsedAlt.width, parsedAlt.height);
|
|
211
|
+
return imageRule(tokens, idx, info, env, self);
|
|
212
|
+
}
|
|
189
213
|
if (cache) {
|
|
190
214
|
const cached = cache.get(src);
|
|
191
|
-
if (cached) return buildImageHTML(
|
|
215
|
+
if (cached) return buildImageHTML(parsedAlt, src, options, cached.width, cached.height, cached.dataURL);
|
|
192
216
|
}
|
|
193
217
|
let width;
|
|
194
218
|
let height;
|
|
@@ -206,25 +230,39 @@ function image(md, userOptions) {
|
|
|
206
230
|
height,
|
|
207
231
|
dataURL: placeholderUrl
|
|
208
232
|
});
|
|
209
|
-
return buildImageHTML(
|
|
233
|
+
return buildImageHTML(parsedAlt, src, options, width, height, placeholderUrl);
|
|
210
234
|
};
|
|
211
235
|
}
|
|
236
|
+
/**
|
|
237
|
+
* Apply user-specified dimensions to existing HTML img tag.
|
|
238
|
+
*/
|
|
239
|
+
function applyDimensionToHTML(html, width, height) {
|
|
240
|
+
if (!width) return html;
|
|
241
|
+
let style = `max-width: ${width}px; width: ${width}px;`;
|
|
242
|
+
if (height) style += ` height: ${height}px;`;
|
|
243
|
+
if (html.includes("<img")) return html.replace(/<img\s/, `<img style="${style}" `);
|
|
244
|
+
return html;
|
|
245
|
+
}
|
|
212
246
|
function generateSrcset(src, width, srcsetWidths) {
|
|
213
247
|
const validWidths = srcsetWidths.filter((w) => w < width).concat(width);
|
|
214
248
|
return Array.from(new Set(validWidths)).sort((a, b) => a - b).map((w) => {
|
|
215
249
|
return `${w === width ? src : src.includes("?") ? `${src}&w=${w}` : `${src}?w=${w}`} ${w}w`;
|
|
216
250
|
}).join(", ");
|
|
217
251
|
}
|
|
218
|
-
function buildImageHTML(
|
|
219
|
-
const
|
|
252
|
+
function buildImageHTML(parsedAlt, src, options, originalWidth, originalHeight, dataURL) {
|
|
253
|
+
const displayWidth = parsedAlt.width || originalWidth;
|
|
254
|
+
const displayHeight = parsedAlt.height;
|
|
255
|
+
const srcset = generateSrcset(src, originalWidth, options.progressive.srcset_widths);
|
|
220
256
|
const mainImgAttrs = [
|
|
221
257
|
`src="${src}"`,
|
|
222
|
-
`alt="${alt}"`,
|
|
258
|
+
`alt="${parsedAlt.alt}"`,
|
|
223
259
|
`srcset="${srcset}"`,
|
|
224
260
|
`loading="lazy" decoding="async"`,
|
|
225
261
|
`style="width: 100%; height: 100%; object-fit: cover; opacity: 0; transition: opacity 0.6s ease-in-out;"`,
|
|
226
262
|
`onload="this.style.opacity=1; setTimeout(() => { this.parentElement.style.backgroundImage='none'; }, 600);"`
|
|
227
263
|
].filter(Boolean).join(" ");
|
|
264
|
+
const aspectWidth = displayWidth;
|
|
265
|
+
const aspectHeight = displayHeight || Math.round(originalHeight / originalWidth * displayWidth);
|
|
228
266
|
/**
|
|
229
267
|
* 3. 返回包装后的 HTML
|
|
230
268
|
* - 只有当有 dataURL 时才包装 div
|
|
@@ -234,11 +272,11 @@ function buildImageHTML(alt, src, options, width, height, dataURL) {
|
|
|
234
272
|
position: relative;
|
|
235
273
|
overflow: hidden;
|
|
236
274
|
width: 100%;
|
|
237
|
-
max-width: ${
|
|
275
|
+
max-width: ${displayWidth}px;
|
|
238
276
|
background-image: url('${dataURL}');
|
|
239
277
|
background-size: cover;
|
|
240
278
|
background-repeat: no-repeat;
|
|
241
|
-
aspect-ratio: ${
|
|
279
|
+
aspect-ratio: ${aspectWidth} / ${aspectHeight};
|
|
242
280
|
">
|
|
243
281
|
<img ${mainImgAttrs}>
|
|
244
282
|
</div>`;
|
|
@@ -246,4 +284,4 @@ function buildImageHTML(alt, src, options, width, height, dataURL) {
|
|
|
246
284
|
}
|
|
247
285
|
|
|
248
286
|
//#endregion
|
|
249
|
-
export { image };
|
|
287
|
+
export { image, parseObsidianImageAlt };
|
package/dist/types.d.mts
CHANGED
|
@@ -1,2 +1,62 @@
|
|
|
1
|
-
|
|
1
|
+
//#region src/types.d.ts
|
|
2
|
+
type CachePathOption = string | null;
|
|
3
|
+
/**
|
|
4
|
+
* Options controlling progressive image loading.
|
|
5
|
+
*/
|
|
6
|
+
interface ProgressiveOptions {
|
|
7
|
+
/**
|
|
8
|
+
* Enable or disable progressive image loading.
|
|
9
|
+
* @default true
|
|
10
|
+
*/
|
|
11
|
+
enable?: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* An array of widths to generate for the srcset attribute.
|
|
14
|
+
* @default [400, 600, 800, 1200, 2000, 3000]
|
|
15
|
+
*/
|
|
16
|
+
srcset_widths?: number[];
|
|
17
|
+
/**
|
|
18
|
+
* Optional value for the sizes attribute.
|
|
19
|
+
* @example "(max-width: 800px) 100vw, 800px"
|
|
20
|
+
*/
|
|
21
|
+
sizes?: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Configuration options consumed by the plugin.
|
|
25
|
+
*/
|
|
26
|
+
interface Options {
|
|
27
|
+
/**
|
|
28
|
+
* Options for progressive image generation.
|
|
29
|
+
*/
|
|
30
|
+
progressive?: ProgressiveOptions;
|
|
31
|
+
/**
|
|
32
|
+
* Domains that support Bitiful API for thumbhash and info.
|
|
33
|
+
* @default ["assets.vluv.space", "s3.bitiful.net", "bitiful.com"]
|
|
34
|
+
*/
|
|
35
|
+
bitiful_domains?: string[];
|
|
36
|
+
/**
|
|
37
|
+
* Array of file format extensions to ignore (case-insensitive).
|
|
38
|
+
* @default ["svg"]
|
|
39
|
+
* @example ["svg", "gif"]
|
|
40
|
+
*/
|
|
41
|
+
ignore_formats?: string[];
|
|
42
|
+
/**
|
|
43
|
+
* Path to the cache file. When unset, caching is disabled.
|
|
44
|
+
* @default null
|
|
45
|
+
* @example ".cache/thumbhash-cache.json"
|
|
46
|
+
*/
|
|
47
|
+
cache_path?: CachePathOption;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Fully resolved options with defaults applied.
|
|
51
|
+
*/
|
|
52
|
+
interface ResolvedOptions extends Options {
|
|
53
|
+
progressive: ProgressiveOptions & {
|
|
54
|
+
enable: boolean;
|
|
55
|
+
srcset_widths: number[];
|
|
56
|
+
};
|
|
57
|
+
bitiful_domains: string[];
|
|
58
|
+
ignore_formats: string[];
|
|
59
|
+
cache_path: CachePathOption;
|
|
60
|
+
}
|
|
61
|
+
//#endregion
|
|
2
62
|
export { CachePathOption, Options, ProgressiveOptions, ResolvedOptions };
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "markdown-exit-s3-image",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"author": "GnixAij",
|
|
5
|
-
"version": "
|
|
5
|
+
"version": "3.0.0",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
"main": "dist/index.mjs",
|
|
23
23
|
"types": "dist/index.d.mts",
|
|
24
24
|
"files": [
|
|
25
|
-
"dist"
|
|
25
|
+
"dist",
|
|
26
|
+
"README.md"
|
|
26
27
|
],
|
|
27
28
|
"engines": {
|
|
28
29
|
"node": ">=20"
|
|
@@ -33,12 +34,12 @@
|
|
|
33
34
|
"test:clean": "rm -f test/cache.json test/output.html"
|
|
34
35
|
},
|
|
35
36
|
"peerDependencies": {
|
|
36
|
-
"markdown-exit": "1.0.0-beta.
|
|
37
|
+
"markdown-exit": "1.0.0-beta.7"
|
|
37
38
|
},
|
|
38
39
|
"devDependencies": {
|
|
39
40
|
"@tsconfig/node20": "^20.1.8",
|
|
40
|
-
"@types/node": "^
|
|
41
|
-
"tsdown": "^0.
|
|
41
|
+
"@types/node": "^25.1.0",
|
|
42
|
+
"tsdown": "^0.20.1",
|
|
42
43
|
"typescript": "^5.9.3"
|
|
43
44
|
},
|
|
44
45
|
"dependencies": {
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
//#region src/types.d.ts
|
|
2
|
-
type CachePathOption = string | null;
|
|
3
|
-
/**
|
|
4
|
-
* Options controlling progressive image loading.
|
|
5
|
-
*/
|
|
6
|
-
interface ProgressiveOptions {
|
|
7
|
-
/**
|
|
8
|
-
* Enable or disable progressive image loading.
|
|
9
|
-
* @default true
|
|
10
|
-
*/
|
|
11
|
-
enable?: boolean;
|
|
12
|
-
/**
|
|
13
|
-
* An array of widths to generate for the srcset attribute.
|
|
14
|
-
* @default [400, 600, 800, 1200, 2000, 3000]
|
|
15
|
-
*/
|
|
16
|
-
srcset_widths?: number[];
|
|
17
|
-
/**
|
|
18
|
-
* Optional value for the sizes attribute.
|
|
19
|
-
* @example "(max-width: 800px) 100vw, 800px"
|
|
20
|
-
*/
|
|
21
|
-
sizes?: string;
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Configuration options consumed by the plugin.
|
|
25
|
-
*/
|
|
26
|
-
interface Options {
|
|
27
|
-
/**
|
|
28
|
-
* Options for progressive image generation.
|
|
29
|
-
*/
|
|
30
|
-
progressive?: ProgressiveOptions;
|
|
31
|
-
/**
|
|
32
|
-
* Domains that support Bitiful API for thumbhash and info.
|
|
33
|
-
* @default ["assets.vluv.space", "s3.bitiful.net", "bitiful.com"]
|
|
34
|
-
*/
|
|
35
|
-
bitiful_domains?: string[];
|
|
36
|
-
/**
|
|
37
|
-
* Array of file format extensions to ignore (case-insensitive).
|
|
38
|
-
* @default ["svg"]
|
|
39
|
-
* @example ["svg", "gif"]
|
|
40
|
-
*/
|
|
41
|
-
ignore_formats?: string[];
|
|
42
|
-
/**
|
|
43
|
-
* Path to the cache file. When unset, caching is disabled.
|
|
44
|
-
* @default null
|
|
45
|
-
* @example ".cache/thumbhash-cache.json"
|
|
46
|
-
*/
|
|
47
|
-
cache_path?: CachePathOption;
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Fully resolved options with defaults applied.
|
|
51
|
-
*/
|
|
52
|
-
interface ResolvedOptions extends Options {
|
|
53
|
-
progressive: ProgressiveOptions & {
|
|
54
|
-
enable: boolean;
|
|
55
|
-
srcset_widths: number[];
|
|
56
|
-
};
|
|
57
|
-
bitiful_domains: string[];
|
|
58
|
-
ignore_formats: string[];
|
|
59
|
-
cache_path: CachePathOption;
|
|
60
|
-
}
|
|
61
|
-
//#endregion
|
|
62
|
-
export { ResolvedOptions as i, Options as n, ProgressiveOptions as r, CachePathOption as t };
|