markdown-exit-s3-image 1.0.2 → 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 -67
- package/dist/types.d.mts +61 -1
- package/package.json +7 -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,6 +1,59 @@
|
|
|
1
|
-
import
|
|
1
|
+
import sharp from "sharp";
|
|
2
2
|
import { thumbHashToDataURL } from "thumbhash";
|
|
3
|
+
import { promises } from "node:fs";
|
|
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
|
+
}
|
|
3
55
|
|
|
56
|
+
//#endregion
|
|
4
57
|
//#region src/cache.ts
|
|
5
58
|
var ImageCache = class {
|
|
6
59
|
cache = {};
|
|
@@ -108,58 +161,26 @@ function resolveOptions(userOptions = {}) {
|
|
|
108
161
|
}
|
|
109
162
|
|
|
110
163
|
//#endregion
|
|
111
|
-
//#region src/
|
|
112
|
-
/**
|
|
113
|
-
* Bitiful CDN integration utilities
|
|
114
|
-
*/
|
|
115
|
-
/**
|
|
116
|
-
* Check if URL belongs to Bitiful CDN.
|
|
117
|
-
*/
|
|
118
|
-
function isBitifulDomain(url, bitifulDomains) {
|
|
119
|
-
try {
|
|
120
|
-
const hostname = new URL(url).hostname;
|
|
121
|
-
return bitifulDomains.some((domain) => hostname.includes(domain));
|
|
122
|
-
} catch {
|
|
123
|
-
return false;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* Fetch dimension info from Bitiful API.
|
|
128
|
-
*/
|
|
129
|
-
async function getBitifulDimension(imageUrl) {
|
|
130
|
-
try {
|
|
131
|
-
const baseUrl = imageUrl.split("?")[0];
|
|
132
|
-
const response = await fetch(`${baseUrl}?fmt=info`);
|
|
133
|
-
if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
134
|
-
const { ImageWidth: width, ImageHeight: height } = await response.json();
|
|
135
|
-
if (typeof width === "number" && typeof height === "number") return {
|
|
136
|
-
width,
|
|
137
|
-
height
|
|
138
|
-
};
|
|
139
|
-
return null;
|
|
140
|
-
} catch (error) {
|
|
141
|
-
console.warn(`[Bitiful] Dimension error for ${imageUrl}:`, error instanceof Error ? error.message : String(error));
|
|
142
|
-
return null;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
164
|
+
//#region src/index.ts
|
|
145
165
|
/**
|
|
146
|
-
*
|
|
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
|
|
147
174
|
*/
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
}
|
|
156
|
-
console.warn(`[Bitiful] Thumbhash error for ${imageUrl}:`, error instanceof Error ? error.message : String(error));
|
|
157
|
-
return null;
|
|
158
|
-
}
|
|
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
|
+
};
|
|
159
183
|
}
|
|
160
|
-
|
|
161
|
-
//#endregion
|
|
162
|
-
//#region src/index.ts
|
|
163
184
|
/**
|
|
164
185
|
* Check if file format should be ignored based on URL extension.
|
|
165
186
|
*/
|
|
@@ -169,24 +190,29 @@ function shouldIgnoreFormat(url, formats) {
|
|
|
169
190
|
}
|
|
170
191
|
function image(md, userOptions) {
|
|
171
192
|
const options = resolveOptions(userOptions);
|
|
172
|
-
if (!options.progressive.enable) return;
|
|
173
193
|
const cachePath = options.cache_path;
|
|
174
|
-
const cache = cachePath ? new ImageCache(cachePath) : null;
|
|
175
|
-
if (cache)
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
+
}
|
|
181
203
|
const imageRule = md.renderer.rules.image;
|
|
182
204
|
if (!imageRule) return;
|
|
183
205
|
md.renderer.rules.image = async (tokens, idx, info, env, self) => {
|
|
184
206
|
const token = tokens[idx];
|
|
185
207
|
const src = token.attrGet("src");
|
|
186
|
-
|
|
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
|
+
}
|
|
187
213
|
if (cache) {
|
|
188
214
|
const cached = cache.get(src);
|
|
189
|
-
if (cached) return buildImageHTML(
|
|
215
|
+
if (cached) return buildImageHTML(parsedAlt, src, options, cached.width, cached.height, cached.dataURL);
|
|
190
216
|
}
|
|
191
217
|
let width;
|
|
192
218
|
let height;
|
|
@@ -204,25 +230,39 @@ function image(md, userOptions) {
|
|
|
204
230
|
height,
|
|
205
231
|
dataURL: placeholderUrl
|
|
206
232
|
});
|
|
207
|
-
return buildImageHTML(
|
|
233
|
+
return buildImageHTML(parsedAlt, src, options, width, height, placeholderUrl);
|
|
208
234
|
};
|
|
209
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
|
+
}
|
|
210
246
|
function generateSrcset(src, width, srcsetWidths) {
|
|
211
247
|
const validWidths = srcsetWidths.filter((w) => w < width).concat(width);
|
|
212
248
|
return Array.from(new Set(validWidths)).sort((a, b) => a - b).map((w) => {
|
|
213
249
|
return `${w === width ? src : src.includes("?") ? `${src}&w=${w}` : `${src}?w=${w}`} ${w}w`;
|
|
214
250
|
}).join(", ");
|
|
215
251
|
}
|
|
216
|
-
function buildImageHTML(
|
|
217
|
-
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);
|
|
218
256
|
const mainImgAttrs = [
|
|
219
257
|
`src="${src}"`,
|
|
220
|
-
`alt="${alt}"`,
|
|
258
|
+
`alt="${parsedAlt.alt}"`,
|
|
221
259
|
`srcset="${srcset}"`,
|
|
222
260
|
`loading="lazy" decoding="async"`,
|
|
223
261
|
`style="width: 100%; height: 100%; object-fit: cover; opacity: 0; transition: opacity 0.6s ease-in-out;"`,
|
|
224
262
|
`onload="this.style.opacity=1; setTimeout(() => { this.parentElement.style.backgroundImage='none'; }, 600);"`
|
|
225
263
|
].filter(Boolean).join(" ");
|
|
264
|
+
const aspectWidth = displayWidth;
|
|
265
|
+
const aspectHeight = displayHeight || Math.round(originalHeight / originalWidth * displayWidth);
|
|
226
266
|
/**
|
|
227
267
|
* 3. 返回包装后的 HTML
|
|
228
268
|
* - 只有当有 dataURL 时才包装 div
|
|
@@ -232,11 +272,11 @@ function buildImageHTML(alt, src, options, width, height, dataURL) {
|
|
|
232
272
|
position: relative;
|
|
233
273
|
overflow: hidden;
|
|
234
274
|
width: 100%;
|
|
235
|
-
max-width: ${
|
|
275
|
+
max-width: ${displayWidth}px;
|
|
236
276
|
background-image: url('${dataURL}');
|
|
237
277
|
background-size: cover;
|
|
238
278
|
background-repeat: no-repeat;
|
|
239
|
-
aspect-ratio: ${
|
|
279
|
+
aspect-ratio: ${aspectWidth} / ${aspectHeight};
|
|
240
280
|
">
|
|
241
281
|
<img ${mainImgAttrs}>
|
|
242
282
|
</div>`;
|
|
@@ -244,4 +284,4 @@ function buildImageHTML(alt, src, options, width, height, dataURL) {
|
|
|
244
284
|
}
|
|
245
285
|
|
|
246
286
|
//#endregion
|
|
247
|
-
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,15 +34,16 @@
|
|
|
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": {
|
|
46
|
+
"sharp": "^0.34.5",
|
|
45
47
|
"thumbhash": "^0.1.1"
|
|
46
48
|
}
|
|
47
49
|
}
|
|
@@ -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 };
|