img-upload-kit 1.1.1 → 2.0.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.
- package/CHANGELOG.md +89 -74
- package/MIGRATION.md +192 -0
- package/README.md +243 -259
- package/dist/bin.js +1186 -0
- package/dist/bin.js.map +1 -0
- package/dist/blur.cjs +40 -412
- package/dist/blur.cjs.map +1 -1
- package/dist/blur.d.cts +5 -5
- package/dist/blur.d.ts +5 -5
- package/dist/blur.js +39 -393
- package/dist/blur.js.map +1 -1
- package/dist/blurDataUtils-DveBRtqE.d.cts +43 -0
- package/dist/blurDataUtils-yx9Q6HlU.d.ts +43 -0
- package/dist/blurService-D5TpMNji.d.ts +83 -0
- package/dist/blurService-DacwylJx.d.cts +83 -0
- package/dist/chunk-25GYKQ6J.js +158 -0
- package/dist/chunk-25GYKQ6J.js.map +1 -0
- package/dist/chunk-34L4TEE3.cjs +226 -0
- package/dist/chunk-34L4TEE3.cjs.map +1 -0
- package/dist/chunk-DFCSN3X7.cjs +158 -0
- package/dist/chunk-DFCSN3X7.cjs.map +1 -0
- package/dist/chunk-EWVIL57J.js +294 -0
- package/dist/chunk-EWVIL57J.js.map +1 -0
- package/dist/chunk-FP5YIWG5.cjs +135 -0
- package/dist/chunk-FP5YIWG5.cjs.map +1 -0
- package/dist/chunk-HILELQGS.js +226 -0
- package/dist/chunk-HILELQGS.js.map +1 -0
- package/dist/chunk-IDLLCVGB.js +2370 -0
- package/dist/chunk-IDLLCVGB.js.map +1 -0
- package/dist/chunk-J6A44Y3W.js +514 -0
- package/dist/chunk-J6A44Y3W.js.map +1 -0
- package/dist/chunk-LOSVFO56.cjs +145 -0
- package/dist/chunk-LOSVFO56.cjs.map +1 -0
- package/dist/chunk-MBQJ6NVJ.cjs +294 -0
- package/dist/chunk-MBQJ6NVJ.cjs.map +1 -0
- package/dist/chunk-MHGQEBPW.js +145 -0
- package/dist/chunk-MHGQEBPW.js.map +1 -0
- package/dist/chunk-QHU7FUO4.cjs +37 -0
- package/dist/chunk-QHU7FUO4.cjs.map +1 -0
- package/dist/chunk-S2IFAWJK.js +37 -0
- package/dist/chunk-S2IFAWJK.js.map +1 -0
- package/dist/chunk-TA5Y64JM.cjs +2370 -0
- package/dist/chunk-TA5Y64JM.cjs.map +1 -0
- package/dist/chunk-VYVZVUNI.js +135 -0
- package/dist/chunk-VYVZVUNI.js.map +1 -0
- package/dist/chunk-XKQMCY5Z.cjs +514 -0
- package/dist/chunk-XKQMCY5Z.cjs.map +1 -0
- package/dist/cli.cjs +273 -76
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +269 -73
- package/dist/cli.js.map +1 -1
- package/dist/headless.cjs +8 -1880
- package/dist/headless.cjs.map +1 -1
- package/dist/headless.d.cts +3 -1
- package/dist/headless.d.ts +3 -1
- package/dist/headless.js +25 -1886
- package/dist/headless.js.map +1 -1
- package/dist/index-BIdnqtW8.d.ts +234 -0
- package/dist/index-Brk_uHs6.d.cts +234 -0
- package/dist/{index-FVXXKf2v.d.ts → index-CPMlaQsC.d.ts} +76 -153
- package/dist/{index-FVXXKf2v.d.cts → index-DHINe-rA.d.cts} +76 -153
- package/dist/index.cjs +64 -532
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -4
- package/dist/index.d.ts +7 -4
- package/dist/index.js +63 -504
- package/dist/index.js.map +1 -1
- package/dist/{memoryAdapter-CBu7_3Pp.d.ts → memoryAdapter-Ck0ccZbF.d.cts} +3 -2
- package/dist/{memoryAdapter-BJjyLR-1.d.cts → memoryAdapter-DxEKaG0b.d.ts} +3 -2
- package/dist/placeholders-C4ftwaII.d.ts +5 -0
- package/dist/placeholders-x_3rt65M.d.cts +5 -0
- package/dist/react.cjs +1952 -3257
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +188 -50
- package/dist/react.d.ts +188 -50
- package/dist/react.js +1763 -3013
- package/dist/react.js.map +1 -1
- package/dist/server-handler.cjs +14 -0
- package/dist/server-handler.cjs.map +1 -0
- package/dist/server-handler.d.cts +3 -0
- package/dist/server-handler.d.ts +3 -0
- package/dist/server-handler.js +14 -0
- package/dist/server-handler.js.map +1 -0
- package/dist/server.cjs +1096 -532
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +77 -154
- package/dist/server.d.ts +77 -154
- package/dist/server.js +1050 -448
- package/dist/server.js.map +1 -1
- package/dist/storage.cjs +239 -232
- package/dist/storage.cjs.map +1 -1
- package/dist/storage.d.cts +21 -5
- package/dist/storage.d.ts +21 -5
- package/dist/storage.js +227 -207
- package/dist/storage.js.map +1 -1
- package/dist/styles/animated-image.css +112 -0
- package/dist/styles/dropzone-and-tiles.css +247 -0
- package/dist/styles/modals-and-dock.css +248 -0
- package/dist/styles/primitives.css +197 -0
- package/dist/styles/quality-and-inputs.css +184 -0
- package/dist/styles/tokens.css +123 -0
- package/dist/styles.css +235 -220
- package/dist/types-BrP-o9nd.d.cts +85 -0
- package/dist/types-sZ3KoFQY.d.ts +85 -0
- package/dist/types-yShGNQnW.d.cts +61 -0
- package/dist/types-yShGNQnW.d.ts +61 -0
- package/dist/{types-CiLY0DwB.d.cts → upload-C5Trd6Dh.d.cts} +27 -53
- package/dist/{types-CiLY0DwB.d.ts → upload-C5Trd6Dh.d.ts} +27 -53
- package/package.json +92 -32
- package/dist/blurService-DqsnfPTd.d.cts +0 -172
- package/dist/blurService-DqsnfPTd.d.ts +0 -172
package/dist/blur.js
CHANGED
|
@@ -1,395 +1,41 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
const blurData = getBlurData(image);
|
|
42
|
-
return {
|
|
43
|
-
backgroundColor: blurData.blurColor,
|
|
44
|
-
backgroundImage: `url(${image.url})`,
|
|
45
|
-
backgroundSize: "cover",
|
|
46
|
-
backgroundPosition: "center",
|
|
47
|
-
backgroundRepeat: "no-repeat"
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
function getBlurPlaceholderStyle(image) {
|
|
51
|
-
const blurData = getBlurData(image);
|
|
52
|
-
if (!blurData.blurDataUrl) {
|
|
53
|
-
return {
|
|
54
|
-
backgroundColor: blurData.blurColor
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
return {
|
|
58
|
-
backgroundColor: blurData.blurColor,
|
|
59
|
-
backgroundImage: `url(${blurData.blurDataUrl})`,
|
|
60
|
-
backgroundSize: "cover",
|
|
61
|
-
backgroundPosition: "center",
|
|
62
|
-
filter: "blur(20px)",
|
|
63
|
-
transform: "scale(1.1)"
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
function hasBlurData(image) {
|
|
67
|
-
return Boolean(image?.blurDataUrl || image?.blurColor || image?.blurHash);
|
|
68
|
-
}
|
|
69
|
-
function getBulkBlurData(images) {
|
|
70
|
-
return images.map(getBlurData);
|
|
71
|
-
}
|
|
72
|
-
function createSrcSetWithBlur(image) {
|
|
73
|
-
if (!image?.url) return "";
|
|
74
|
-
const blurData = getBlurData(image);
|
|
75
|
-
if (!blurData.blurDataUrl) {
|
|
76
|
-
return image.url;
|
|
77
|
-
}
|
|
78
|
-
return `${blurData.blurDataUrl} 40w, ${image.url} ${image.width || 1920}w`;
|
|
79
|
-
}
|
|
80
|
-
function preloadImageWithBlur(image) {
|
|
81
|
-
if (!image?.url || typeof document === "undefined") return;
|
|
82
|
-
const link = document.createElement("link");
|
|
83
|
-
link.rel = "preload";
|
|
84
|
-
link.as = "image";
|
|
85
|
-
link.href = image.url;
|
|
86
|
-
const blurData = getBlurData(image);
|
|
87
|
-
if (blurData.blurDataUrl) {
|
|
88
|
-
link.setAttribute("imagesrcset", blurData.blurDataUrl);
|
|
89
|
-
}
|
|
90
|
-
document.head.appendChild(link);
|
|
91
|
-
}
|
|
92
|
-
function extractImageUrls(image) {
|
|
93
|
-
if (!image?.url) return [];
|
|
94
|
-
return [image.url];
|
|
95
|
-
}
|
|
96
|
-
function decodeBlurHashToDataURL(hash, width = 32, height = 32, punch = 1) {
|
|
97
|
-
if (!hash || typeof document === "undefined") return null;
|
|
98
|
-
const validation = isBlurhashValid(hash);
|
|
99
|
-
if (!validation.result) return null;
|
|
100
|
-
try {
|
|
101
|
-
const pixels = decode(hash, width, height, punch);
|
|
102
|
-
const canvas = document.createElement("canvas");
|
|
103
|
-
canvas.width = width;
|
|
104
|
-
canvas.height = height;
|
|
105
|
-
const ctx = canvas.getContext("2d");
|
|
106
|
-
if (!ctx) return null;
|
|
107
|
-
const imageData = ctx.createImageData(width, height);
|
|
108
|
-
imageData.data.set(pixels);
|
|
109
|
-
ctx.putImageData(imageData, 0, 0);
|
|
110
|
-
return canvas.toDataURL("image/png");
|
|
111
|
-
} catch {
|
|
112
|
-
return null;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// src/blur/lruCache.ts
|
|
117
|
-
var LRUCache = class {
|
|
118
|
-
maxSize;
|
|
119
|
-
cache;
|
|
120
|
-
constructor(maxSize = 500) {
|
|
121
|
-
this.maxSize = Math.max(1, maxSize);
|
|
122
|
-
this.cache = /* @__PURE__ */ new Map();
|
|
123
|
-
}
|
|
124
|
-
get(key) {
|
|
125
|
-
if (!this.cache.has(key)) return void 0;
|
|
126
|
-
const value = this.cache.get(key);
|
|
127
|
-
this.cache.delete(key);
|
|
128
|
-
this.cache.set(key, value);
|
|
129
|
-
return value;
|
|
130
|
-
}
|
|
131
|
-
set(key, value) {
|
|
132
|
-
if (this.cache.has(key)) {
|
|
133
|
-
this.cache.delete(key);
|
|
134
|
-
}
|
|
135
|
-
if (this.cache.size >= this.maxSize) {
|
|
136
|
-
const firstKey = this.cache.keys().next().value;
|
|
137
|
-
if (firstKey !== void 0) {
|
|
138
|
-
this.cache.delete(firstKey);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
this.cache.set(key, value);
|
|
142
|
-
}
|
|
143
|
-
has(key) {
|
|
144
|
-
return this.cache.has(key);
|
|
145
|
-
}
|
|
146
|
-
clear() {
|
|
147
|
-
this.cache.clear();
|
|
148
|
-
}
|
|
149
|
-
get size() {
|
|
150
|
-
return this.cache.size;
|
|
151
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
BlurService,
|
|
3
|
+
DEFAULT_CDN_PATTERNS,
|
|
4
|
+
DEFAULT_PLACEHOLDERS,
|
|
5
|
+
LRUCache,
|
|
6
|
+
blurService,
|
|
7
|
+
createBlurService,
|
|
8
|
+
normalizeImagePath
|
|
9
|
+
} from "./chunk-EWVIL57J.js";
|
|
10
|
+
import {
|
|
11
|
+
createSrcSetWithBlur,
|
|
12
|
+
decodeBlurHashToDataURL,
|
|
13
|
+
extractImageUrls,
|
|
14
|
+
getBackgroundPropsWithBlur,
|
|
15
|
+
getBlurData,
|
|
16
|
+
getBlurPlaceholderStyle,
|
|
17
|
+
getBulkBlurData,
|
|
18
|
+
getImagePropsWithBlur,
|
|
19
|
+
hasBlurData,
|
|
20
|
+
preloadImageWithBlur
|
|
21
|
+
} from "./chunk-VYVZVUNI.js";
|
|
22
|
+
export {
|
|
23
|
+
BlurService,
|
|
24
|
+
DEFAULT_CDN_PATTERNS,
|
|
25
|
+
DEFAULT_PLACEHOLDERS,
|
|
26
|
+
LRUCache,
|
|
27
|
+
blurService,
|
|
28
|
+
createBlurService,
|
|
29
|
+
createSrcSetWithBlur,
|
|
30
|
+
decodeBlurHashToDataURL,
|
|
31
|
+
extractImageUrls,
|
|
32
|
+
getBackgroundPropsWithBlur,
|
|
33
|
+
getBlurData,
|
|
34
|
+
getBlurPlaceholderStyle,
|
|
35
|
+
getBulkBlurData,
|
|
36
|
+
getImagePropsWithBlur,
|
|
37
|
+
hasBlurData,
|
|
38
|
+
normalizeImagePath,
|
|
39
|
+
preloadImageWithBlur
|
|
152
40
|
};
|
|
153
|
-
|
|
154
|
-
// src/blur/pathMapper.ts
|
|
155
|
-
var DEFAULT_CDN_PATTERNS = [
|
|
156
|
-
/^https?:\/\/[^/]+\.supabase\.co\/storage\/v1\/object\/public\/([^/]+)\/(.+)$/,
|
|
157
|
-
/^https?:\/\/[^/]+\.r2\.dev\/(.+)$/,
|
|
158
|
-
/^https?:\/\/[^/]+\.amazonaws\.com\/(.+)$/,
|
|
159
|
-
/^https?:\/\/[^/]+\.cloudflare\.com\/(.+)$/,
|
|
160
|
-
/^https?:\/\/cdn\.[^/]+\/(.+)$/
|
|
161
|
-
];
|
|
162
|
-
function normalizeImagePath(imageSrc, options = {}) {
|
|
163
|
-
const originalPath = imageSrc;
|
|
164
|
-
let normalizedPath = imageSrc;
|
|
165
|
-
let isExternal = false;
|
|
166
|
-
try {
|
|
167
|
-
if (imageSrc.startsWith("data:") || imageSrc.startsWith("blob:")) {
|
|
168
|
-
return {
|
|
169
|
-
normalizedPath: imageSrc,
|
|
170
|
-
isExternal: false,
|
|
171
|
-
originalPath
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
const patterns = options.cdnPatterns || DEFAULT_CDN_PATTERNS;
|
|
175
|
-
if (imageSrc.startsWith("http://") || imageSrc.startsWith("https://")) {
|
|
176
|
-
const url = new URL(imageSrc);
|
|
177
|
-
isExternal = true;
|
|
178
|
-
const cleanUrlNoQuery = `${url.origin}${url.pathname}`;
|
|
179
|
-
for (const pattern of patterns) {
|
|
180
|
-
const match = cleanUrlNoQuery.match(pattern);
|
|
181
|
-
if (match) {
|
|
182
|
-
const pathPart = match[match.length - 1];
|
|
183
|
-
normalizedPath = "/" + pathPart.replace(/^\/+/, "");
|
|
184
|
-
break;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
if (normalizedPath === imageSrc) {
|
|
188
|
-
normalizedPath = url.pathname;
|
|
189
|
-
}
|
|
190
|
-
} else {
|
|
191
|
-
normalizedPath = normalizedPath.split("?")[0].split("#")[0];
|
|
192
|
-
}
|
|
193
|
-
normalizedPath = normalizedPath.replace(/\\/g, "/").replace(/^\.\.?\//g, "");
|
|
194
|
-
if (!normalizedPath.startsWith("/")) {
|
|
195
|
-
normalizedPath = "/" + normalizedPath;
|
|
196
|
-
}
|
|
197
|
-
const prefixesToStrip = options.stripPrefixes || ["/public/"];
|
|
198
|
-
for (const prefix of prefixesToStrip) {
|
|
199
|
-
if (normalizedPath.startsWith(prefix)) {
|
|
200
|
-
normalizedPath = "/" + normalizedPath.slice(prefix.length).replace(/^\/+/, "");
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
const segments = normalizedPath.split("/").filter(Boolean);
|
|
204
|
-
const filename = segments[segments.length - 1];
|
|
205
|
-
const folder = segments.length > 1 ? segments.slice(0, -1).join("/") : void 0;
|
|
206
|
-
const dotIdx = filename ? filename.lastIndexOf(".") : -1;
|
|
207
|
-
const extension = filename && dotIdx > 0 ? filename.slice(dotIdx + 1).toLowerCase() : void 0;
|
|
208
|
-
return {
|
|
209
|
-
normalizedPath,
|
|
210
|
-
folder,
|
|
211
|
-
filename,
|
|
212
|
-
extension,
|
|
213
|
-
isExternal,
|
|
214
|
-
originalPath
|
|
215
|
-
};
|
|
216
|
-
} catch {
|
|
217
|
-
return {
|
|
218
|
-
normalizedPath: imageSrc,
|
|
219
|
-
isExternal: false,
|
|
220
|
-
originalPath
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// src/blur/placeholders.ts
|
|
226
|
-
var DEFAULT_PLACEHOLDERS = {
|
|
227
|
-
default: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTUwIiBoZWlnaHQ9IjE1MCIgdmlld0JveD0iMCAwIDE1MCAxNTAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkaWVudCIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3R5bGU9InN0b3AtY29sb3I6IzJhMzIzZDtzdG9wLW9wYWNpdHk6MSIvPjxzdG9wIG9mZnNldD0iNTAlIiBzdHlsZT0ic3RvcC1jb2xvcjojMjEyNzMwO3N0b3Atb3BhY2l0eToxIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdHlsZT0ic3RvcC1jb2xvcjojMTgxZDI0O3N0b3Atb3BhY2l0eToxIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3Qgd2lkdGg9IjE1MCIgaGVpZ2h0PSIxNTAiIGZpbGw9InVybCgjZ3JhZGllbnQpIi8+PC9zdmc+",
|
|
228
|
-
hero: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTkyMCIgaGVpZ2h0PSIxMDgwIiB2aWV3Qm94PSIwIDAgMTkyMCAxMDgwIiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD0iaGVyb0dyYWRpZW50IiB4MT0iMCUiIHkxPSIwJSIgeDI9IjEwMCUiIHkyPSIxMDAlIj48c3RvcCBvZmZzZXQ9IjAlIiBzdHlsZT0ic3RvcC1jb2xvcjojMmQzNzQ4O3N0b3Atb3BhY2l0eToxIi8+PHN0b3Agb2Zmc2V0PSIzMCUiIHN0eWxlPSJzdG9wLWNvbG9yOiMyMzJiMzg7c3RvcC1vcGFjaXR5OjEiLz48c3RvcCBvZmZzZXQ9IjcwJSIgc3R5bGU9InN0b3AtY29sb3I6IzFlMjUzMDtzdG9wLW9wYWNpdHk6MSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3R5bGU9InN0b3AtY29sb3I6IzE3MWQyNztzdG9wLW9wYWNpdHk6MSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHdpZHRoPSIxOTIwIiBoZWlnaHQ9IjEwODAiIGZpbGw9InVybCgjaGVyb0dyYWRpZW50KSIvPjwvc3ZnPg==",
|
|
229
|
-
card: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAwIiBoZWlnaHQ9IjMwMCIgdmlld0JveD0iMCAwIDQwMCAzMDAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJjYXJkR3JhZGllbnQiIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0eWxlPSJzdG9wLWNvbG9yOiMyYTMyM2Q7c3RvcC1vcGFjaXR5OjEiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0eWxlPSJzdG9wLWNvbG9yOiMxYzIyMmI7c3RvcC1vcGFjaXR5OjEiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48cmVjdCB3aWR0aD0iNDAwIiBoZWlnaHQ9IjMwMCIgZmlsbD0idXJsKCNjYXJkR3JhZGllbnQpIi8+PC9zdmc+",
|
|
230
|
-
thumbnail: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgdmlld0JveD0iMCAwIDEwMCAxMDAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHJlY3Qgd2lkdGg9IjEwMCIgaGVpZ2h0PSIxMDAiIGZpbGw9IiMyNDJiMzUiLz48L3N2Zz4="
|
|
231
|
-
};
|
|
232
|
-
|
|
233
|
-
// src/blur/blurService.ts
|
|
234
|
-
var BlurService = class {
|
|
235
|
-
blurData;
|
|
236
|
-
cache;
|
|
237
|
-
pathOptions;
|
|
238
|
-
metadata;
|
|
239
|
-
metrics;
|
|
240
|
-
constructor(options = {}) {
|
|
241
|
-
this.blurData = {};
|
|
242
|
-
this.pathOptions = options.pathOptions;
|
|
243
|
-
this.metadata = {
|
|
244
|
-
version: "2.0",
|
|
245
|
-
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
246
|
-
totalImages: 0
|
|
247
|
-
};
|
|
248
|
-
this.cache = new LRUCache(options.cacheSize ?? 500);
|
|
249
|
-
this.metrics = {
|
|
250
|
-
totalRequests: 0,
|
|
251
|
-
cacheHits: 0,
|
|
252
|
-
cacheMisses: 0,
|
|
253
|
-
errors: 0
|
|
254
|
-
};
|
|
255
|
-
if (options.manifest) {
|
|
256
|
-
this.loadManifest(options.manifest);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
/**
|
|
260
|
-
* Hydrate the service with a generated `blur-hashes.json` manifest or hash map.
|
|
261
|
-
*/
|
|
262
|
-
loadManifest(manifest) {
|
|
263
|
-
this.cache.clear();
|
|
264
|
-
if ("hashes" in manifest && typeof manifest.hashes === "object" && manifest.hashes !== null) {
|
|
265
|
-
const typed = manifest;
|
|
266
|
-
this.blurData = { ...typed.hashes };
|
|
267
|
-
this.metadata = {
|
|
268
|
-
version: typed.version || "2.0",
|
|
269
|
-
generatedAt: typed.generatedAt || (/* @__PURE__ */ new Date()).toISOString(),
|
|
270
|
-
totalImages: Object.keys(this.blurData).length
|
|
271
|
-
};
|
|
272
|
-
} else {
|
|
273
|
-
this.blurData = { ...manifest };
|
|
274
|
-
this.metadata = {
|
|
275
|
-
version: "2.0",
|
|
276
|
-
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
277
|
-
totalImages: Object.keys(this.blurData).length
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
/**
|
|
282
|
-
* Register or update a single image's blur data at runtime.
|
|
283
|
-
*/
|
|
284
|
-
registerEntry(imagePath, entry) {
|
|
285
|
-
this.blurData[imagePath] = entry;
|
|
286
|
-
this.metadata.totalImages = Object.keys(this.blurData).length;
|
|
287
|
-
this.cache.clear();
|
|
288
|
-
}
|
|
289
|
-
getDefaultPlaceholder(type = "default") {
|
|
290
|
-
return DEFAULT_PLACEHOLDERS[type];
|
|
291
|
-
}
|
|
292
|
-
findBlurDataByPath(imageSrc) {
|
|
293
|
-
let data = this.blurData[imageSrc];
|
|
294
|
-
if (data) return data;
|
|
295
|
-
const pathInfo = normalizeImagePath(imageSrc, this.pathOptions);
|
|
296
|
-
data = this.blurData[pathInfo.normalizedPath];
|
|
297
|
-
if (data) return data;
|
|
298
|
-
const withSlash = pathInfo.normalizedPath.startsWith("/") ? pathInfo.normalizedPath : "/" + pathInfo.normalizedPath;
|
|
299
|
-
const withoutSlash = pathInfo.normalizedPath.replace(/^\/+/, "");
|
|
300
|
-
data = this.blurData[withSlash] || this.blurData[withoutSlash];
|
|
301
|
-
if (data) return data;
|
|
302
|
-
if (pathInfo.isExternal && pathInfo.originalPath !== imageSrc) {
|
|
303
|
-
data = this.blurData[pathInfo.originalPath];
|
|
304
|
-
if (data) return data;
|
|
305
|
-
}
|
|
306
|
-
return null;
|
|
307
|
-
}
|
|
308
|
-
getDominantColor(imageSrc) {
|
|
309
|
-
const data = this.findBlurDataByPath(imageSrc);
|
|
310
|
-
return data?.color || "#E5E7EB";
|
|
311
|
-
}
|
|
312
|
-
getBlurDataURL(imageSrc, options = {}) {
|
|
313
|
-
const { type = "default", size = "small" } = options;
|
|
314
|
-
this.metrics.totalRequests++;
|
|
315
|
-
const cacheKey = `${imageSrc}-${size}`;
|
|
316
|
-
if (this.cache.has(cacheKey)) {
|
|
317
|
-
this.metrics.cacheHits++;
|
|
318
|
-
return this.cache.get(cacheKey).dataUrl;
|
|
319
|
-
}
|
|
320
|
-
this.metrics.cacheMisses++;
|
|
321
|
-
if (Object.keys(this.blurData).length === 0) {
|
|
322
|
-
return this.getDefaultPlaceholder(type);
|
|
323
|
-
}
|
|
324
|
-
const data = this.findBlurDataByPath(imageSrc);
|
|
325
|
-
if (!data) {
|
|
326
|
-
return this.getDefaultPlaceholder(type);
|
|
327
|
-
}
|
|
328
|
-
const blurDataURL = data.variants?.[size] || data.variants?.small || this.getDefaultPlaceholder(type);
|
|
329
|
-
this.cache.set(cacheKey, {
|
|
330
|
-
dataUrl: blurDataURL,
|
|
331
|
-
timestamp: Date.now()
|
|
332
|
-
});
|
|
333
|
-
return blurDataURL;
|
|
334
|
-
}
|
|
335
|
-
getBulkBlurData(imageSrcs, options = {}) {
|
|
336
|
-
const result = {};
|
|
337
|
-
for (const imageSrc of imageSrcs) {
|
|
338
|
-
result[imageSrc] = this.getBlurDataURL(imageSrc, options);
|
|
339
|
-
}
|
|
340
|
-
return result;
|
|
341
|
-
}
|
|
342
|
-
getProgressiveData(imageSrc, size = "small") {
|
|
343
|
-
return {
|
|
344
|
-
color: this.getDominantColor(imageSrc),
|
|
345
|
-
blur: this.getBlurDataURL(imageSrc, { size }),
|
|
346
|
-
hasBlur: this.hasBlurHash(imageSrc)
|
|
347
|
-
};
|
|
348
|
-
}
|
|
349
|
-
hasBlurHash(imageSrc) {
|
|
350
|
-
return Boolean(this.findBlurDataByPath(imageSrc));
|
|
351
|
-
}
|
|
352
|
-
getMetadata() {
|
|
353
|
-
const cacheHitRate = this.metrics.totalRequests > 0 ? (this.metrics.cacheHits / this.metrics.totalRequests * 100).toFixed(2) : "0.00";
|
|
354
|
-
return {
|
|
355
|
-
...this.metadata,
|
|
356
|
-
cacheSize: this.cache.size,
|
|
357
|
-
metrics: {
|
|
358
|
-
...this.metrics,
|
|
359
|
-
cacheHitRate: `${cacheHitRate}%`
|
|
360
|
-
}
|
|
361
|
-
};
|
|
362
|
-
}
|
|
363
|
-
clearCache() {
|
|
364
|
-
this.cache.clear();
|
|
365
|
-
}
|
|
366
|
-
getBlurHash(imageSrc) {
|
|
367
|
-
const data = this.findBlurDataByPath(imageSrc);
|
|
368
|
-
return data?.hash || null;
|
|
369
|
-
}
|
|
370
|
-
preloadBlurData(imageSrcs, size = "small") {
|
|
371
|
-
for (const imageSrc of imageSrcs) {
|
|
372
|
-
this.getBlurDataURL(imageSrc, { size });
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
async fetchBlurDataForImage(imageSrc, type = "default") {
|
|
376
|
-
return Promise.resolve(this.getBlurDataURL(imageSrc, { type }));
|
|
377
|
-
}
|
|
378
|
-
getAvailableKeys() {
|
|
379
|
-
return Object.keys(this.blurData);
|
|
380
|
-
}
|
|
381
|
-
findMatchingKeys(searchTerm) {
|
|
382
|
-
const lower = searchTerm.toLowerCase();
|
|
383
|
-
return Object.keys(this.blurData).filter(
|
|
384
|
-
(key) => key.toLowerCase().includes(lower)
|
|
385
|
-
);
|
|
386
|
-
}
|
|
387
|
-
};
|
|
388
|
-
function createBlurService(options) {
|
|
389
|
-
return new BlurService(options);
|
|
390
|
-
}
|
|
391
|
-
var blurService = new BlurService();
|
|
392
|
-
|
|
393
|
-
export { BlurService, DEFAULT_CDN_PATTERNS, DEFAULT_PLACEHOLDERS, LRUCache, blurService, createBlurService, createSrcSetWithBlur, decodeBlurHashToDataURL, extractImageUrls, getBackgroundPropsWithBlur, getBlurData, getBlurPlaceholderStyle, getBulkBlurData, getImagePropsWithBlur, hasBlurData, normalizeImagePath, preloadImageWithBlur };
|
|
394
|
-
//# sourceMappingURL=blur.js.map
|
|
395
41
|
//# sourceMappingURL=blur.js.map
|
package/dist/blur.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/blur/blurDataUtils.ts","../src/blur/lruCache.ts","../src/blur/pathMapper.ts","../src/blur/placeholders.ts","../src/blur/blurService.ts"],"names":["decodeBlurHash"],"mappings":";;;AAaO,SAAS,YACd,KAAA,EACU;AACV,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,OAAO;AAAA,MACL,WAAA,EAAa,EAAA;AAAA,MACb,SAAA,EAAW,SAAA;AAAA,MACX,OAAA,EAAS;AAAA,KACX;AAAA,EACF;AAEA,EAAA,OAAO;AAAA,IACL,WAAA,EAAa,MAAM,WAAA,IAAe,EAAA;AAAA,IAClC,SAAA,EAAW,MAAM,SAAA,IAAa,SAAA;AAAA,IAC9B,GAAI,MAAM,QAAA,GAAW,EAAE,UAAU,KAAA,CAAM,QAAA,KAAa,EAAC;AAAA,IACrD,OAAA,EAAS,OAAA,CAAQ,KAAA,CAAM,WAAA,IAAe,MAAM,QAAQ;AAAA,GACtD;AACF;AAEO,SAAS,sBACd,KAAA,EACA;AACA,EAAA,IAAI,CAAC,OAAO,GAAA,EAAK;AACf,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,MAAM,QAAA,GAAW,YAAY,KAAK,CAAA;AAElC,EAAA,OAAO;AAAA,IACL,KAAK,KAAA,CAAM,GAAA;AAAA,IACX,GAAA,EAAK,MAAM,OAAA,IAAW,EAAA;AAAA,IACtB,KAAA,EAAO,MAAM,KAAA,IAAS,IAAA;AAAA,IACtB,MAAA,EAAQ,MAAM,MAAA,IAAU,IAAA;AAAA,IACxB,GAAI,SAAS,WAAA,IAAe;AAAA,MAC1B,WAAA,EAAa,MAAA;AAAA,MACb,aAAa,QAAA,CAAS;AAAA;AACxB,GACF;AACF;AAEO,SAAS,2BACd,KAAA,EACsB;AACtB,EAAA,IAAI,CAAC,OAAO,GAAA,EAAK;AACf,IAAA,OAAO;AAAA,MACL,eAAA,EAAiB;AAAA,KACnB;AAAA,EACF;AAEA,EAAA,MAAM,QAAA,GAAW,YAAY,KAAK,CAAA;AAElC,EAAA,OAAO;AAAA,IACL,iBAAiB,QAAA,CAAS,SAAA;AAAA,IAC1B,eAAA,EAAiB,CAAA,IAAA,EAAO,KAAA,CAAM,GAAG,CAAA,CAAA,CAAA;AAAA,IACjC,cAAA,EAAgB,OAAA;AAAA,IAChB,kBAAA,EAAoB,QAAA;AAAA,IACpB,gBAAA,EAAkB;AAAA,GACpB;AACF;AAEO,SAAS,wBACd,KAAA,EACsB;AACtB,EAAA,MAAM,QAAA,GAAW,YAAY,KAAK,CAAA;AAElC,EAAA,IAAI,CAAC,SAAS,WAAA,EAAa;AACzB,IAAA,OAAO;AAAA,MACL,iBAAiB,QAAA,CAAS;AAAA,KAC5B;AAAA,EACF;AAEA,EAAA,OAAO;AAAA,IACL,iBAAiB,QAAA,CAAS,SAAA;AAAA,IAC1B,eAAA,EAAiB,CAAA,IAAA,EAAO,QAAA,CAAS,WAAW,CAAA,CAAA,CAAA;AAAA,IAC5C,cAAA,EAAgB,OAAA;AAAA,IAChB,kBAAA,EAAoB,QAAA;AAAA,IACpB,MAAA,EAAQ,YAAA;AAAA,IACR,SAAA,EAAW;AAAA,GACb;AACF;AAEO,SAAS,YACd,KAAA,EACS;AACT,EAAA,OAAO,QAAQ,KAAA,EAAO,WAAA,IAAe,KAAA,EAAO,SAAA,IAAa,OAAO,QAAQ,CAAA;AAC1E;AAEO,SAAS,gBACd,MAAA,EACY;AACZ,EAAA,OAAO,MAAA,CAAO,IAAI,WAAW,CAAA;AAC/B;AAEO,SAAS,qBACd,KAAA,EACQ;AACR,EAAA,IAAI,CAAC,KAAA,EAAO,GAAA,EAAK,OAAO,EAAA;AAExB,EAAA,MAAM,QAAA,GAAW,YAAY,KAAK,CAAA;AAClC,EAAA,IAAI,CAAC,SAAS,WAAA,EAAa;AACzB,IAAA,OAAO,KAAA,CAAM,GAAA;AAAA,EACf;AAEA,EAAA,OAAO,CAAA,EAAG,SAAS,WAAW,CAAA,MAAA,EAAS,MAAM,GAAG,CAAA,CAAA,EAAI,KAAA,CAAM,KAAA,IAAS,IAAI,CAAA,CAAA,CAAA;AACzE;AAEO,SAAS,qBACd,KAAA,EACM;AACN,EAAA,IAAI,CAAC,KAAA,EAAO,GAAA,IAAO,OAAO,aAAa,WAAA,EAAa;AAEpD,EAAA,MAAM,IAAA,GAAO,QAAA,CAAS,aAAA,CAAc,MAAM,CAAA;AAC1C,EAAA,IAAA,CAAK,GAAA,GAAM,SAAA;AACX,EAAA,IAAA,CAAK,EAAA,GAAK,OAAA;AACV,EAAA,IAAA,CAAK,OAAO,KAAA,CAAM,GAAA;AAElB,EAAA,MAAM,QAAA,GAAW,YAAY,KAAK,CAAA;AAClC,EAAA,IAAI,SAAS,WAAA,EAAa;AACxB,IAAA,IAAA,CAAK,YAAA,CAAa,aAAA,EAAe,QAAA,CAAS,WAAW,CAAA;AAAA,EACvD;AAEA,EAAA,QAAA,CAAS,IAAA,CAAK,YAAY,IAAI,CAAA;AAChC;AAEO,SAAS,iBACd,KAAA,EACU;AACV,EAAA,IAAI,CAAC,KAAA,EAAO,GAAA,EAAK,OAAO,EAAC;AACzB,EAAA,OAAO,CAAC,MAAM,GAAG,CAAA;AACnB;AAMO,SAAS,wBACd,IAAA,EACA,KAAA,GAAgB,IAChB,MAAA,GAAiB,EAAA,EACjB,QAAgB,CAAA,EACD;AACf,EAAA,IAAI,CAAC,IAAA,IAAQ,OAAO,QAAA,KAAa,aAAa,OAAO,IAAA;AACrD,EAAA,MAAM,UAAA,GAAa,gBAAgB,IAAI,CAAA;AACvC,EAAA,IAAI,CAAC,UAAA,CAAW,MAAA,EAAQ,OAAO,IAAA;AAE/B,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,GAASA,MAAA,CAAe,IAAA,EAAM,KAAA,EAAO,QAAQ,KAAK,CAAA;AACxD,IAAA,MAAM,MAAA,GAAS,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AAC9C,IAAA,MAAA,CAAO,KAAA,GAAQ,KAAA;AACf,IAAA,MAAA,CAAO,MAAA,GAAS,MAAA;AAChB,IAAA,MAAM,GAAA,GAAM,MAAA,CAAO,UAAA,CAAW,IAAI,CAAA;AAClC,IAAA,IAAI,CAAC,KAAK,OAAO,IAAA;AACjB,IAAA,MAAM,SAAA,GAAY,GAAA,CAAI,eAAA,CAAgB,KAAA,EAAO,MAAM,CAAA;AACnD,IAAA,SAAA,CAAU,IAAA,CAAK,IAAI,MAAM,CAAA;AACzB,IAAA,GAAA,CAAI,YAAA,CAAa,SAAA,EAAW,CAAA,EAAG,CAAC,CAAA;AAChC,IAAA,OAAO,MAAA,CAAO,UAAU,WAAW,CAAA;AAAA,EACrC,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;;;AC5KO,IAAM,WAAN,MAAqB;AAAA,EACT,OAAA;AAAA,EACA,KAAA;AAAA,EAEjB,WAAA,CAAY,UAAkB,GAAA,EAAK;AACjC,IAAA,IAAA,CAAK,OAAA,GAAU,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,OAAO,CAAA;AAClC,IAAA,IAAA,CAAK,KAAA,uBAAY,GAAA,EAAI;AAAA,EACvB;AAAA,EAEA,IAAI,GAAA,EAAuB;AACzB,IAAA,IAAI,CAAC,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,GAAG,GAAG,OAAO,MAAA;AAEjC,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,GAAG,CAAA;AAChC,IAAA,IAAA,CAAK,KAAA,CAAM,OAAO,GAAG,CAAA;AACrB,IAAA,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,GAAA,EAAK,KAAK,CAAA;AACzB,IAAA,OAAO,KAAA;AAAA,EACT;AAAA,EAEA,GAAA,CAAI,KAAQ,KAAA,EAAgB;AAC1B,IAAA,IAAI,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,GAAG,CAAA,EAAG;AACvB,MAAA,IAAA,CAAK,KAAA,CAAM,OAAO,GAAG,CAAA;AAAA,IACvB;AAEA,IAAA,IAAI,IAAA,CAAK,KAAA,CAAM,IAAA,IAAQ,IAAA,CAAK,OAAA,EAAS;AACnC,MAAA,MAAM,WAAW,IAAA,CAAK,KAAA,CAAM,IAAA,EAAK,CAAE,MAAK,CAAE,KAAA;AAC1C,MAAA,IAAI,aAAa,MAAA,EAAW;AAC1B,QAAA,IAAA,CAAK,KAAA,CAAM,OAAO,QAAQ,CAAA;AAAA,MAC5B;AAAA,IACF;AAEA,IAAA,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,GAAA,EAAK,KAAK,CAAA;AAAA,EAC3B;AAAA,EAEA,IAAI,GAAA,EAAiB;AACnB,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,GAAG,CAAA;AAAA,EAC3B;AAAA,EAEA,KAAA,GAAc;AACZ,IAAA,IAAA,CAAK,MAAM,KAAA,EAAM;AAAA,EACnB;AAAA,EAEA,IAAI,IAAA,GAAe;AACjB,IAAA,OAAO,KAAK,KAAA,CAAM,IAAA;AAAA,EACpB;AACF;;;AC5CO,IAAM,oBAAA,GAAiC;AAAA,EAC5C,8EAAA;AAAA,EACA,mCAAA;AAAA,EACA,0CAAA;AAAA,EACA,2CAAA;AAAA,EACA;AACF;AAqBO,SAAS,kBAAA,CACd,QAAA,EACA,OAAA,GAAgC,EAAC,EACvB;AACV,EAAA,MAAM,YAAA,GAAe,QAAA;AACrB,EAAA,IAAI,cAAA,GAAiB,QAAA;AACrB,EAAA,IAAI,UAAA,GAAa,KAAA;AAEjB,EAAA,IAAI;AACF,IAAA,IAAI,SAAS,UAAA,CAAW,OAAO,KAAK,QAAA,CAAS,UAAA,CAAW,OAAO,CAAA,EAAG;AAChE,MAAA,OAAO;AAAA,QACL,cAAA,EAAgB,QAAA;AAAA,QAChB,UAAA,EAAY,KAAA;AAAA,QACZ;AAAA,OACF;AAAA,IACF;AAEA,IAAA,MAAM,QAAA,GAAW,QAAQ,WAAA,IAAe,oBAAA;AAExC,IAAA,IAAI,SAAS,UAAA,CAAW,SAAS,KAAK,QAAA,CAAS,UAAA,CAAW,UAAU,CAAA,EAAG;AACrE,MAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,QAAQ,CAAA;AAC5B,MAAA,UAAA,GAAa,IAAA;AACb,MAAA,MAAM,kBAAkB,CAAA,EAAG,GAAA,CAAI,MAAM,CAAA,EAAG,IAAI,QAAQ,CAAA,CAAA;AAEpD,MAAA,KAAA,MAAW,WAAW,QAAA,EAAU;AAC9B,QAAA,MAAM,KAAA,GAAQ,eAAA,CAAgB,KAAA,CAAM,OAAO,CAAA;AAC3C,QAAA,IAAI,KAAA,EAAO;AACT,UAAA,MAAM,QAAA,GAAW,KAAA,CAAM,KAAA,CAAM,MAAA,GAAS,CAAC,CAAA;AACvC,UAAA,cAAA,GAAiB,GAAA,GAAM,QAAA,CAAS,OAAA,CAAQ,MAAA,EAAQ,EAAE,CAAA;AAClD,UAAA;AAAA,QACF;AAAA,MACF;AAEA,MAAA,IAAI,mBAAmB,QAAA,EAAU;AAC/B,QAAA,cAAA,GAAiB,GAAA,CAAI,QAAA;AAAA,MACvB;AAAA,IACF,CAAA,MAAO;AACL,MAAA,cAAA,GAAiB,cAAA,CAAe,MAAM,GAAG,CAAA,CAAE,CAAC,CAAA,CAAE,KAAA,CAAM,GAAG,CAAA,CAAE,CAAC,CAAA;AAAA,IAC5D;AAEA,IAAA,cAAA,GAAiB,eAAe,OAAA,CAAQ,KAAA,EAAO,GAAG,CAAA,CAAE,OAAA,CAAQ,aAAa,EAAE,CAAA;AAE3E,IAAA,IAAI,CAAC,cAAA,CAAe,UAAA,CAAW,GAAG,CAAA,EAAG;AACnC,MAAA,cAAA,GAAiB,GAAA,GAAM,cAAA;AAAA,IACzB;AAEA,IAAA,MAAM,eAAA,GAAkB,OAAA,CAAQ,aAAA,IAAiB,CAAC,UAAU,CAAA;AAC5D,IAAA,KAAA,MAAW,UAAU,eAAA,EAAiB;AACpC,MAAA,IAAI,cAAA,CAAe,UAAA,CAAW,MAAM,CAAA,EAAG;AACrC,QAAA,cAAA,GAAiB,GAAA,GAAM,eAAe,KAAA,CAAM,MAAA,CAAO,MAAM,CAAA,CAAE,OAAA,CAAQ,QAAQ,EAAE,CAAA;AAAA,MAC/E;AAAA,IACF;AAEA,IAAA,MAAM,WAAW,cAAA,CAAe,KAAA,CAAM,GAAG,CAAA,CAAE,OAAO,OAAO,CAAA;AACzD,IAAA,MAAM,QAAA,GAAW,QAAA,CAAS,QAAA,CAAS,MAAA,GAAS,CAAC,CAAA;AAC7C,IAAA,MAAM,MAAA,GACJ,QAAA,CAAS,MAAA,GAAS,CAAA,GAAI,QAAA,CAAS,KAAA,CAAM,CAAA,EAAG,CAAA,CAAE,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA,GAAI,KAAA,CAAA;AAC1D,IAAA,MAAM,MAAA,GAAS,QAAA,GAAW,QAAA,CAAS,WAAA,CAAY,GAAG,CAAA,GAAI,CAAA,CAAA;AACtD,IAAA,MAAM,SAAA,GACJ,QAAA,IAAY,MAAA,GAAS,CAAA,GAAI,QAAA,CAAS,MAAM,MAAA,GAAS,CAAC,CAAA,CAAE,WAAA,EAAY,GAAI,KAAA,CAAA;AAEtE,IAAA,OAAO;AAAA,MACL,cAAA;AAAA,MACA,MAAA;AAAA,MACA,QAAA;AAAA,MACA,SAAA;AAAA,MACA,UAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO;AAAA,MACL,cAAA,EAAgB,QAAA;AAAA,MAChB,UAAA,EAAY,KAAA;AAAA,MACZ;AAAA,KACF;AAAA,EACF;AACF;;;ACrGO,IAAM,oBAAA,GAA4D;AAAA,EACvE,OAAA,EACE,wmBAAA;AAAA,EACF,IAAA,EACE,gtBAAA;AAAA,EACF,IAAA,EACE,giBAAA;AAAA,EACF,SAAA,EACE;AACJ;;;ACOO,IAAM,cAAN,MAAkB;AAAA,EACf,QAAA;AAAA,EACS,KAAA;AAAA,EACA,WAAA;AAAA,EACT,QAAA;AAAA,EAKA,OAAA;AAAA,EAER,WAAA,CAAY,OAAA,GAA8B,EAAC,EAAG;AAC5C,IAAA,IAAA,CAAK,WAAW,EAAC;AACjB,IAAA,IAAA,CAAK,cAAc,OAAA,CAAQ,WAAA;AAC3B,IAAA,IAAA,CAAK,QAAA,GAAW;AAAA,MACd,OAAA,EAAS,KAAA;AAAA,MACT,WAAA,EAAA,iBAAa,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AAAA,MACpC,WAAA,EAAa;AAAA,KACf;AACA,IAAA,IAAA,CAAK,KAAA,GAAQ,IAAI,QAAA,CAAS,OAAA,CAAQ,aAAa,GAAG,CAAA;AAClD,IAAA,IAAA,CAAK,OAAA,GAAU;AAAA,MACb,aAAA,EAAe,CAAA;AAAA,MACf,SAAA,EAAW,CAAA;AAAA,MACX,WAAA,EAAa,CAAA;AAAA,MACb,MAAA,EAAQ;AAAA,KACV;AAEA,IAAA,IAAI,QAAQ,QAAA,EAAU;AACpB,MAAA,IAAA,CAAK,YAAA,CAAa,QAAQ,QAAQ,CAAA;AAAA,IACpC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,aACE,QAAA,EACM;AACN,IAAA,IAAA,CAAK,MAAM,KAAA,EAAM;AAEjB,IAAA,IAAI,QAAA,IAAY,YAAY,OAAO,QAAA,CAAS,WAAW,QAAA,IAAY,QAAA,CAAS,WAAW,IAAA,EAAM;AAC3F,MAAA,MAAM,KAAA,GAAQ,QAAA;AACd,MAAA,IAAA,CAAK,QAAA,GAAW,EAAE,GAAG,KAAA,CAAM,MAAA,EAAO;AAClC,MAAA,IAAA,CAAK,QAAA,GAAW;AAAA,QACd,OAAA,EAAS,MAAM,OAAA,IAAW,KAAA;AAAA,QAC1B,aAAa,KAAA,CAAM,WAAA,IAAA,iBAAe,IAAI,IAAA,IAAO,WAAA,EAAY;AAAA,QACzD,WAAA,EAAa,MAAA,CAAO,IAAA,CAAK,IAAA,CAAK,QAAQ,CAAA,CAAE;AAAA,OAC1C;AAAA,IACF,CAAA,MAAO;AACL,MAAA,IAAA,CAAK,QAAA,GAAW,EAAE,GAAI,QAAA,EAA+C;AACrE,MAAA,IAAA,CAAK,QAAA,GAAW;AAAA,QACd,OAAA,EAAS,KAAA;AAAA,QACT,WAAA,EAAA,iBAAa,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AAAA,QACpC,WAAA,EAAa,MAAA,CAAO,IAAA,CAAK,IAAA,CAAK,QAAQ,CAAA,CAAE;AAAA,OAC1C;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,aAAA,CAAc,WAAmB,KAAA,EAAgC;AAC/D,IAAA,IAAA,CAAK,QAAA,CAAS,SAAS,CAAA,GAAI,KAAA;AAC3B,IAAA,IAAA,CAAK,SAAS,WAAA,GAAc,MAAA,CAAO,IAAA,CAAK,IAAA,CAAK,QAAQ,CAAA,CAAE,MAAA;AACvD,IAAA,IAAA,CAAK,MAAM,KAAA,EAAM;AAAA,EACnB;AAAA,EAEA,qBAAA,CAAsB,OAA4B,SAAA,EAAmB;AACnE,IAAA,OAAO,qBAAqB,IAAI,CAAA;AAAA,EAClC;AAAA,EAEQ,mBAAmB,QAAA,EAA4C;AACrE,IAAA,IAAI,IAAA,GAAO,IAAA,CAAK,QAAA,CAAS,QAAQ,CAAA;AACjC,IAAA,IAAI,MAAM,OAAO,IAAA;AAEjB,IAAA,MAAM,QAAA,GAAW,kBAAA,CAAmB,QAAA,EAAU,IAAA,CAAK,WAAW,CAAA;AAC9D,IAAA,IAAA,GAAO,IAAA,CAAK,QAAA,CAAS,QAAA,CAAS,cAAc,CAAA;AAC5C,IAAA,IAAI,MAAM,OAAO,IAAA;AAEjB,IAAA,MAAM,SAAA,GAAY,SAAS,cAAA,CAAe,UAAA,CAAW,GAAG,CAAA,GACpD,QAAA,CAAS,cAAA,GACT,GAAA,GAAM,QAAA,CAAS,cAAA;AACnB,IAAA,MAAM,YAAA,GAAe,QAAA,CAAS,cAAA,CAAe,OAAA,CAAQ,QAAQ,EAAE,CAAA;AAE/D,IAAA,IAAA,GAAO,KAAK,QAAA,CAAS,SAAS,CAAA,IAAK,IAAA,CAAK,SAAS,YAAY,CAAA;AAC7D,IAAA,IAAI,MAAM,OAAO,IAAA;AAEjB,IAAA,IAAI,QAAA,CAAS,UAAA,IAAc,QAAA,CAAS,YAAA,KAAiB,QAAA,EAAU;AAC7D,MAAA,IAAA,GAAO,IAAA,CAAK,QAAA,CAAS,QAAA,CAAS,YAAY,CAAA;AAC1C,MAAA,IAAI,MAAM,OAAO,IAAA;AAAA,IACnB;AAEA,IAAA,OAAO,IAAA;AAAA,EACT;AAAA,EAEA,iBAAiB,QAAA,EAA0B;AACzC,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,kBAAA,CAAmB,QAAQ,CAAA;AAC7C,IAAA,OAAO,MAAM,KAAA,IAAS,SAAA;AAAA,EACxB;AAAA,EAEA,cAAA,CACE,QAAA,EACA,OAAA,GAGI,EAAC,EACG;AACR,IAAA,MAAM,EAAE,IAAA,GAAO,SAAA,EAAW,IAAA,GAAO,SAAQ,GAAI,OAAA;AAE7C,IAAA,IAAA,CAAK,OAAA,CAAQ,aAAA,EAAA;AAEb,IAAA,MAAM,QAAA,GAAW,CAAA,EAAG,QAAQ,CAAA,CAAA,EAAI,IAAI,CAAA,CAAA;AACpC,IAAA,IAAI,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,QAAQ,CAAA,EAAG;AAC5B,MAAA,IAAA,CAAK,OAAA,CAAQ,SAAA,EAAA;AACb,MAAA,OAAO,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,QAAQ,CAAA,CAAG,OAAA;AAAA,IACnC;AAEA,IAAA,IAAA,CAAK,OAAA,CAAQ,WAAA,EAAA;AAEb,IAAA,IAAI,OAAO,IAAA,CAAK,IAAA,CAAK,QAAQ,CAAA,CAAE,WAAW,CAAA,EAAG;AAC3C,MAAA,OAAO,IAAA,CAAK,sBAAsB,IAAI,CAAA;AAAA,IACxC;AAEA,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,kBAAA,CAAmB,QAAQ,CAAA;AAC7C,IAAA,IAAI,CAAC,IAAA,EAAM;AACT,MAAA,OAAO,IAAA,CAAK,sBAAsB,IAAI,CAAA;AAAA,IACxC;AAEA,IAAA,MAAM,WAAA,GACJ,IAAA,CAAK,QAAA,GAAW,IAAI,CAAA,IACpB,KAAK,QAAA,EAAU,KAAA,IACf,IAAA,CAAK,qBAAA,CAAsB,IAAI,CAAA;AAEjC,IAAA,IAAA,CAAK,KAAA,CAAM,IAAI,QAAA,EAAU;AAAA,MACvB,OAAA,EAAS,WAAA;AAAA,MACT,SAAA,EAAW,KAAK,GAAA;AAAI,KACrB,CAAA;AAED,IAAA,OAAO,WAAA;AAAA,EACT;AAAA,EAEA,eAAA,CACE,SAAA,EACA,OAAA,GAGI,EAAC,EACmB;AACxB,IAAA,MAAM,SAAiC,EAAC;AACxC,IAAA,KAAA,MAAW,YAAY,SAAA,EAAW;AAChC,MAAA,MAAA,CAAO,QAAQ,CAAA,GAAI,IAAA,CAAK,cAAA,CAAe,UAAU,OAAO,CAAA;AAAA,IAC1D;AACA,IAAA,OAAO,MAAA;AAAA,EACT;AAAA,EAEA,kBAAA,CAAmB,QAAA,EAAkB,IAAA,GAAiB,OAAA,EAAS;AAC7D,IAAA,OAAO;AAAA,MACL,KAAA,EAAO,IAAA,CAAK,gBAAA,CAAiB,QAAQ,CAAA;AAAA,MACrC,MAAM,IAAA,CAAK,cAAA,CAAe,QAAA,EAAU,EAAE,MAAM,CAAA;AAAA,MAC5C,OAAA,EAAS,IAAA,CAAK,WAAA,CAAY,QAAQ;AAAA,KACpC;AAAA,EACF;AAAA,EAEA,YAAY,QAAA,EAA2B;AACrC,IAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,kBAAA,CAAmB,QAAQ,CAAC,CAAA;AAAA,EAClD;AAAA,EAEA,WAAA,GAAc;AACZ,IAAA,MAAM,YAAA,GACJ,IAAA,CAAK,OAAA,CAAQ,aAAA,GAAgB,KAEtB,IAAA,CAAK,OAAA,CAAQ,SAAA,GAAY,IAAA,CAAK,OAAA,CAAQ,aAAA,GACvC,GAAA,EACA,OAAA,CAAQ,CAAC,CAAA,GACX,MAAA;AAEN,IAAA,OAAO;AAAA,MACL,GAAG,IAAA,CAAK,QAAA;AAAA,MACR,SAAA,EAAW,KAAK,KAAA,CAAM,IAAA;AAAA,MACtB,OAAA,EAAS;AAAA,QACP,GAAG,IAAA,CAAK,OAAA;AAAA,QACR,YAAA,EAAc,GAAG,YAAY,CAAA,CAAA;AAAA;AAC/B,KACF;AAAA,EACF;AAAA,EAEA,UAAA,GAAmB;AACjB,IAAA,IAAA,CAAK,MAAM,KAAA,EAAM;AAAA,EACnB;AAAA,EAEA,YAAY,QAAA,EAAiC;AAC3C,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,kBAAA,CAAmB,QAAQ,CAAA;AAC7C,IAAA,OAAO,MAAM,IAAA,IAAQ,IAAA;AAAA,EACvB;AAAA,EAEA,eAAA,CAAgB,SAAA,EAAqB,IAAA,GAAiB,OAAA,EAAe;AACnE,IAAA,KAAA,MAAW,YAAY,SAAA,EAAW;AAChC,MAAA,IAAA,CAAK,cAAA,CAAe,QAAA,EAAU,EAAE,IAAA,EAAM,CAAA;AAAA,IACxC;AAAA,EACF;AAAA,EAEA,MAAM,qBAAA,CACJ,QAAA,EACA,IAAA,GAA4B,SAAA,EACX;AACjB,IAAA,OAAO,OAAA,CAAQ,QAAQ,IAAA,CAAK,cAAA,CAAe,UAAU,EAAE,IAAA,EAAM,CAAC,CAAA;AAAA,EAChE;AAAA,EAEA,gBAAA,GAA6B;AAC3B,IAAA,OAAO,MAAA,CAAO,IAAA,CAAK,IAAA,CAAK,QAAQ,CAAA;AAAA,EAClC;AAAA,EAEA,iBAAiB,UAAA,EAA8B;AAC7C,IAAA,MAAM,KAAA,GAAQ,WAAW,WAAA,EAAY;AACrC,IAAA,OAAO,MAAA,CAAO,IAAA,CAAK,IAAA,CAAK,QAAQ,CAAA,CAAE,MAAA;AAAA,MAAO,CAAC,GAAA,KACxC,GAAA,CAAI,WAAA,EAAY,CAAE,SAAS,KAAK;AAAA,KAClC;AAAA,EACF;AACF;AAEO,SAAS,kBAAkB,OAAA,EAA2C;AAC3E,EAAA,OAAO,IAAI,YAAY,OAAO,CAAA;AAChC;AAEO,IAAM,WAAA,GAAc,IAAI,WAAA","file":"blur.js","sourcesContent":["import { decode as decodeBlurHash, isBlurhashValid } from 'blurhash';\nimport type { BlurData, BlurImageSource } from './types';\n\nexport interface BlurPlaceholderStyle {\n backgroundColor: string;\n backgroundImage?: string;\n backgroundSize?: string;\n backgroundPosition?: string;\n backgroundRepeat?: string;\n filter?: string;\n transform?: string;\n}\n\nexport function getBlurData(\n image: BlurImageSource | null | undefined,\n): BlurData {\n if (!image) {\n return {\n blurDataUrl: '',\n blurColor: '#E5E7EB',\n hasBlur: false,\n };\n }\n\n return {\n blurDataUrl: image.blurDataUrl || '',\n blurColor: image.blurColor || '#E5E7EB',\n ...(image.blurHash ? { blurHash: image.blurHash } : {}),\n hasBlur: Boolean(image.blurDataUrl || image.blurHash),\n };\n}\n\nexport function getImagePropsWithBlur(\n image: BlurImageSource | null | undefined,\n) {\n if (!image?.url) {\n return null;\n }\n\n const blurData = getBlurData(image);\n\n return {\n src: image.url,\n alt: image.altText || '',\n width: image.width || 1920,\n height: image.height || 1080,\n ...(blurData.blurDataUrl && {\n placeholder: 'blur' as const,\n blurDataURL: blurData.blurDataUrl,\n }),\n };\n}\n\nexport function getBackgroundPropsWithBlur(\n image: BlurImageSource | null | undefined,\n): BlurPlaceholderStyle {\n if (!image?.url) {\n return {\n backgroundColor: '#E5E7EB',\n };\n }\n\n const blurData = getBlurData(image);\n\n return {\n backgroundColor: blurData.blurColor,\n backgroundImage: `url(${image.url})`,\n backgroundSize: 'cover',\n backgroundPosition: 'center',\n backgroundRepeat: 'no-repeat',\n };\n}\n\nexport function getBlurPlaceholderStyle(\n image: BlurImageSource | null | undefined,\n): BlurPlaceholderStyle {\n const blurData = getBlurData(image);\n\n if (!blurData.blurDataUrl) {\n return {\n backgroundColor: blurData.blurColor,\n };\n }\n\n return {\n backgroundColor: blurData.blurColor,\n backgroundImage: `url(${blurData.blurDataUrl})`,\n backgroundSize: 'cover',\n backgroundPosition: 'center',\n filter: 'blur(20px)',\n transform: 'scale(1.1)',\n };\n}\n\nexport function hasBlurData(\n image: BlurImageSource | null | undefined,\n): boolean {\n return Boolean(image?.blurDataUrl || image?.blurColor || image?.blurHash);\n}\n\nexport function getBulkBlurData(\n images: Array<BlurImageSource | null | undefined>,\n): BlurData[] {\n return images.map(getBlurData);\n}\n\nexport function createSrcSetWithBlur(\n image: BlurImageSource | null | undefined,\n): string {\n if (!image?.url) return '';\n\n const blurData = getBlurData(image);\n if (!blurData.blurDataUrl) {\n return image.url;\n }\n\n return `${blurData.blurDataUrl} 40w, ${image.url} ${image.width || 1920}w`;\n}\n\nexport function preloadImageWithBlur(\n image: BlurImageSource | null | undefined,\n): void {\n if (!image?.url || typeof document === 'undefined') return;\n\n const link = document.createElement('link');\n link.rel = 'preload';\n link.as = 'image';\n link.href = image.url;\n\n const blurData = getBlurData(image);\n if (blurData.blurDataUrl) {\n link.setAttribute('imagesrcset', blurData.blurDataUrl);\n }\n\n document.head.appendChild(link);\n}\n\nexport function extractImageUrls(\n image: BlurImageSource | null | undefined,\n): string[] {\n if (!image?.url) return [];\n return [image.url];\n}\n\n/**\n * Browser helper: Decodes a compact BlurHash string (e.g. \"LEHV6nWB2yk8pyo0adR*.7kCMdnj\")\n * into a `data:image/png;base64,...` URL using an offscreen `<canvas>`.\n */\nexport function decodeBlurHashToDataURL(\n hash: string,\n width: number = 32,\n height: number = 32,\n punch: number = 1,\n): string | null {\n if (!hash || typeof document === 'undefined') return null;\n const validation = isBlurhashValid(hash);\n if (!validation.result) return null;\n\n try {\n const pixels = decodeBlurHash(hash, width, height, punch);\n const canvas = document.createElement('canvas');\n canvas.width = width;\n canvas.height = height;\n const ctx = canvas.getContext('2d');\n if (!ctx) return null;\n const imageData = ctx.createImageData(width, height);\n imageData.data.set(pixels);\n ctx.putImageData(imageData, 0, 0);\n return canvas.toDataURL('image/png');\n } catch {\n return null;\n }\n}\n","export class LRUCache<K, V> {\n private readonly maxSize: number;\n private readonly cache: Map<K, V>;\n\n constructor(maxSize: number = 500) {\n this.maxSize = Math.max(1, maxSize);\n this.cache = new Map();\n }\n\n get(key: K): V | undefined {\n if (!this.cache.has(key)) return undefined;\n\n const value = this.cache.get(key)!;\n this.cache.delete(key);\n this.cache.set(key, value);\n return value;\n }\n\n set(key: K, value: V): void {\n if (this.cache.has(key)) {\n this.cache.delete(key);\n }\n\n if (this.cache.size >= this.maxSize) {\n const firstKey = this.cache.keys().next().value;\n if (firstKey !== undefined) {\n this.cache.delete(firstKey);\n }\n }\n\n this.cache.set(key, value);\n }\n\n has(key: K): boolean {\n return this.cache.has(key);\n }\n\n clear(): void {\n this.cache.clear();\n }\n\n get size(): number {\n return this.cache.size;\n }\n}\n","export const DEFAULT_CDN_PATTERNS: RegExp[] = [\n /^https?:\\/\\/[^/]+\\.supabase\\.co\\/storage\\/v1\\/object\\/public\\/([^/]+)\\/(.+)$/,\n /^https?:\\/\\/[^/]+\\.r2\\.dev\\/(.+)$/,\n /^https?:\\/\\/[^/]+\\.amazonaws\\.com\\/(.+)$/,\n /^https?:\\/\\/[^/]+\\.cloudflare\\.com\\/(.+)$/,\n /^https?:\\/\\/cdn\\.[^/]+\\/(.+)$/,\n];\n\nexport interface NormalizePathOptions {\n cdnPatterns?: RegExp[];\n stripPrefixes?: string[];\n}\n\nexport interface PathInfo {\n normalizedPath: string;\n folder?: string;\n filename?: string;\n extension?: string;\n isExternal: boolean;\n originalPath: string;\n}\n\n/**\n * Project-agnostic image URL / path normalizer.\n * Strips query parameters, leading `/public/`, relative `./`, and common CDN prefixes\n * so manifest keys match consistently across local and CDN environments.\n */\nexport function normalizeImagePath(\n imageSrc: string,\n options: NormalizePathOptions = {},\n): PathInfo {\n const originalPath = imageSrc;\n let normalizedPath = imageSrc;\n let isExternal = false;\n\n try {\n if (imageSrc.startsWith('data:') || imageSrc.startsWith('blob:')) {\n return {\n normalizedPath: imageSrc,\n isExternal: false,\n originalPath,\n };\n }\n\n const patterns = options.cdnPatterns || DEFAULT_CDN_PATTERNS;\n\n if (imageSrc.startsWith('http://') || imageSrc.startsWith('https://')) {\n const url = new URL(imageSrc);\n isExternal = true;\n const cleanUrlNoQuery = `${url.origin}${url.pathname}`;\n\n for (const pattern of patterns) {\n const match = cleanUrlNoQuery.match(pattern);\n if (match) {\n const pathPart = match[match.length - 1];\n normalizedPath = '/' + pathPart.replace(/^\\/+/, '');\n break;\n }\n }\n\n if (normalizedPath === imageSrc) {\n normalizedPath = url.pathname;\n }\n } else {\n normalizedPath = normalizedPath.split('?')[0].split('#')[0];\n }\n\n normalizedPath = normalizedPath.replace(/\\\\/g, '/').replace(/^\\.\\.?\\//g, '');\n\n if (!normalizedPath.startsWith('/')) {\n normalizedPath = '/' + normalizedPath;\n }\n\n const prefixesToStrip = options.stripPrefixes || ['/public/'];\n for (const prefix of prefixesToStrip) {\n if (normalizedPath.startsWith(prefix)) {\n normalizedPath = '/' + normalizedPath.slice(prefix.length).replace(/^\\/+/, '');\n }\n }\n\n const segments = normalizedPath.split('/').filter(Boolean);\n const filename = segments[segments.length - 1];\n const folder =\n segments.length > 1 ? segments.slice(0, -1).join('/') : undefined;\n const dotIdx = filename ? filename.lastIndexOf('.') : -1;\n const extension =\n filename && dotIdx > 0 ? filename.slice(dotIdx + 1).toLowerCase() : undefined;\n\n return {\n normalizedPath,\n folder,\n filename,\n extension,\n isExternal,\n originalPath,\n };\n } catch {\n return {\n normalizedPath: imageSrc,\n isExternal: false,\n originalPath,\n };\n }\n}\n","import type { BlurPlaceholderType } from './types';\n\nexport const DEFAULT_PLACEHOLDERS: Record<BlurPlaceholderType, string> = {\n default:\n 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTUwIiBoZWlnaHQ9IjE1MCIgdmlld0JveD0iMCAwIDE1MCAxNTAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkaWVudCIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3R5bGU9InN0b3AtY29sb3I6IzJhMzIzZDtzdG9wLW9wYWNpdHk6MSIvPjxzdG9wIG9mZnNldD0iNTAlIiBzdHlsZT0ic3RvcC1jb2xvcjojMjEyNzMwO3N0b3Atb3BhY2l0eToxIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdHlsZT0ic3RvcC1jb2xvcjojMTgxZDI0O3N0b3Atb3BhY2l0eToxIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3Qgd2lkdGg9IjE1MCIgaGVpZ2h0PSIxNTAiIGZpbGw9InVybCgjZ3JhZGllbnQpIi8+PC9zdmc+',\n hero:\n 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTkyMCIgaGVpZ2h0PSIxMDgwIiB2aWV3Qm94PSIwIDAgMTkyMCAxMDgwIiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD0iaGVyb0dyYWRpZW50IiB4MT0iMCUiIHkxPSIwJSIgeDI9IjEwMCUiIHkyPSIxMDAlIj48c3RvcCBvZmZzZXQ9IjAlIiBzdHlsZT0ic3RvcC1jb2xvcjojMmQzNzQ4O3N0b3Atb3BhY2l0eToxIi8+PHN0b3Agb2Zmc2V0PSIzMCUiIHN0eWxlPSJzdG9wLWNvbG9yOiMyMzJiMzg7c3RvcC1vcGFjaXR5OjEiLz48c3RvcCBvZmZzZXQ9IjcwJSIgc3R5bGU9InN0b3AtY29sb3I6IzFlMjUzMDtzdG9wLW9wYWNpdHk6MSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3R5bGU9InN0b3AtY29sb3I6IzE3MWQyNztzdG9wLW9wYWNpdHk6MSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHdpZHRoPSIxOTIwIiBoZWlnaHQ9IjEwODAiIGZpbGw9InVybCgjaGVyb0dyYWRpZW50KSIvPjwvc3ZnPg==',\n card:\n 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAwIiBoZWlnaHQ9IjMwMCIgdmlld0JveD0iMCAwIDQwMCAzMDAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJjYXJkR3JhZGllbnQiIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0eWxlPSJzdG9wLWNvbG9yOiMyYTMyM2Q7c3RvcC1vcGFjaXR5OjEiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0eWxlPSJzdG9wLWNvbG9yOiMxYzIyMmI7c3RvcC1vcGFjaXR5OjEiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48cmVjdCB3aWR0aD0iNDAwIiBoZWlnaHQ9IjMwMCIgZmlsbD0idXJsKCNjYXJkR3JhZGllbnQpIi8+PC9zdmc+',\n thumbnail:\n 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgdmlld0JveD0iMCAwIDEwMCAxMDAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHJlY3Qgd2lkdGg9IjEwMCIgaGVpZ2h0PSIxMDAiIGZpbGw9IiMyNDJiMzUiLz48L3N2Zz4=',\n};\n","import { LRUCache } from './lruCache';\nimport { normalizeImagePath, type NormalizePathOptions } from './pathMapper';\nimport { DEFAULT_PLACEHOLDERS } from './placeholders';\nimport type {\n BlurCacheEntry,\n BlurManifestData,\n BlurManifestEntry,\n BlurPlaceholderType,\n BlurServiceMetrics,\n BlurSize,\n} from './types';\n\nexport interface BlurServiceOptions {\n cacheSize?: number;\n manifest?: BlurManifestData | Record<string, BlurManifestEntry>;\n pathOptions?: NormalizePathOptions;\n}\n\nexport class BlurService {\n private blurData: Record<string, BlurManifestEntry>;\n private readonly cache: LRUCache<string, BlurCacheEntry>;\n private readonly pathOptions?: NormalizePathOptions;\n private metadata: {\n version: string;\n generatedAt: string;\n totalImages: number;\n };\n private metrics: BlurServiceMetrics;\n\n constructor(options: BlurServiceOptions = {}) {\n this.blurData = {};\n this.pathOptions = options.pathOptions;\n this.metadata = {\n version: '2.0',\n generatedAt: new Date().toISOString(),\n totalImages: 0,\n };\n this.cache = new LRUCache(options.cacheSize ?? 500);\n this.metrics = {\n totalRequests: 0,\n cacheHits: 0,\n cacheMisses: 0,\n errors: 0,\n };\n\n if (options.manifest) {\n this.loadManifest(options.manifest);\n }\n }\n\n /**\n * Hydrate the service with a generated `blur-hashes.json` manifest or hash map.\n */\n loadManifest(\n manifest: BlurManifestData | Record<string, BlurManifestEntry>,\n ): void {\n this.cache.clear();\n\n if ('hashes' in manifest && typeof manifest.hashes === 'object' && manifest.hashes !== null) {\n const typed = manifest as BlurManifestData;\n this.blurData = { ...typed.hashes };\n this.metadata = {\n version: typed.version || '2.0',\n generatedAt: typed.generatedAt || new Date().toISOString(),\n totalImages: Object.keys(this.blurData).length,\n };\n } else {\n this.blurData = { ...(manifest as Record<string, BlurManifestEntry>) };\n this.metadata = {\n version: '2.0',\n generatedAt: new Date().toISOString(),\n totalImages: Object.keys(this.blurData).length,\n };\n }\n }\n\n /**\n * Register or update a single image's blur data at runtime.\n */\n registerEntry(imagePath: string, entry: BlurManifestEntry): void {\n this.blurData[imagePath] = entry;\n this.metadata.totalImages = Object.keys(this.blurData).length;\n this.cache.clear();\n }\n\n getDefaultPlaceholder(type: BlurPlaceholderType = 'default'): string {\n return DEFAULT_PLACEHOLDERS[type];\n }\n\n private findBlurDataByPath(imageSrc: string): BlurManifestEntry | null {\n let data = this.blurData[imageSrc];\n if (data) return data;\n\n const pathInfo = normalizeImagePath(imageSrc, this.pathOptions);\n data = this.blurData[pathInfo.normalizedPath];\n if (data) return data;\n\n const withSlash = pathInfo.normalizedPath.startsWith('/')\n ? pathInfo.normalizedPath\n : '/' + pathInfo.normalizedPath;\n const withoutSlash = pathInfo.normalizedPath.replace(/^\\/+/, '');\n\n data = this.blurData[withSlash] || this.blurData[withoutSlash];\n if (data) return data;\n\n if (pathInfo.isExternal && pathInfo.originalPath !== imageSrc) {\n data = this.blurData[pathInfo.originalPath];\n if (data) return data;\n }\n\n return null;\n }\n\n getDominantColor(imageSrc: string): string {\n const data = this.findBlurDataByPath(imageSrc);\n return data?.color || '#E5E7EB';\n }\n\n getBlurDataURL(\n imageSrc: string,\n options: {\n type?: BlurPlaceholderType;\n size?: BlurSize;\n } = {},\n ): string {\n const { type = 'default', size = 'small' } = options;\n\n this.metrics.totalRequests++;\n\n const cacheKey = `${imageSrc}-${size}`;\n if (this.cache.has(cacheKey)) {\n this.metrics.cacheHits++;\n return this.cache.get(cacheKey)!.dataUrl;\n }\n\n this.metrics.cacheMisses++;\n\n if (Object.keys(this.blurData).length === 0) {\n return this.getDefaultPlaceholder(type);\n }\n\n const data = this.findBlurDataByPath(imageSrc);\n if (!data) {\n return this.getDefaultPlaceholder(type);\n }\n\n const blurDataURL =\n data.variants?.[size] ||\n data.variants?.small ||\n this.getDefaultPlaceholder(type);\n\n this.cache.set(cacheKey, {\n dataUrl: blurDataURL,\n timestamp: Date.now(),\n });\n\n return blurDataURL;\n }\n\n getBulkBlurData(\n imageSrcs: string[],\n options: {\n type?: BlurPlaceholderType;\n size?: BlurSize;\n } = {},\n ): Record<string, string> {\n const result: Record<string, string> = {};\n for (const imageSrc of imageSrcs) {\n result[imageSrc] = this.getBlurDataURL(imageSrc, options);\n }\n return result;\n }\n\n getProgressiveData(imageSrc: string, size: BlurSize = 'small') {\n return {\n color: this.getDominantColor(imageSrc),\n blur: this.getBlurDataURL(imageSrc, { size }),\n hasBlur: this.hasBlurHash(imageSrc),\n };\n }\n\n hasBlurHash(imageSrc: string): boolean {\n return Boolean(this.findBlurDataByPath(imageSrc));\n }\n\n getMetadata() {\n const cacheHitRate =\n this.metrics.totalRequests > 0\n ? (\n (this.metrics.cacheHits / this.metrics.totalRequests) *\n 100\n ).toFixed(2)\n : '0.00';\n\n return {\n ...this.metadata,\n cacheSize: this.cache.size,\n metrics: {\n ...this.metrics,\n cacheHitRate: `${cacheHitRate}%`,\n },\n };\n }\n\n clearCache(): void {\n this.cache.clear();\n }\n\n getBlurHash(imageSrc: string): string | null {\n const data = this.findBlurDataByPath(imageSrc);\n return data?.hash || null;\n }\n\n preloadBlurData(imageSrcs: string[], size: BlurSize = 'small'): void {\n for (const imageSrc of imageSrcs) {\n this.getBlurDataURL(imageSrc, { size });\n }\n }\n\n async fetchBlurDataForImage(\n imageSrc: string,\n type: BlurPlaceholderType = 'default',\n ): Promise<string> {\n return Promise.resolve(this.getBlurDataURL(imageSrc, { type }));\n }\n\n getAvailableKeys(): string[] {\n return Object.keys(this.blurData);\n }\n\n findMatchingKeys(searchTerm: string): string[] {\n const lower = searchTerm.toLowerCase();\n return Object.keys(this.blurData).filter((key) =>\n key.toLowerCase().includes(lower),\n );\n }\n}\n\nexport function createBlurService(options?: BlurServiceOptions): BlurService {\n return new BlurService(options);\n}\n\nexport const blurService = new BlurService();\n"]}
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { c as BlurImageSource, a as BlurData } from './types-yShGNQnW.cjs';
|
|
2
|
+
|
|
3
|
+
interface BlurPlaceholderStyle {
|
|
4
|
+
backgroundColor: string;
|
|
5
|
+
backgroundImage?: string;
|
|
6
|
+
backgroundSize?: string;
|
|
7
|
+
backgroundPosition?: string;
|
|
8
|
+
backgroundRepeat?: string;
|
|
9
|
+
filter?: string;
|
|
10
|
+
transform?: string;
|
|
11
|
+
}
|
|
12
|
+
declare function getBlurData(image: BlurImageSource | null | undefined): BlurData;
|
|
13
|
+
declare function getImagePropsWithBlur(image: BlurImageSource | null | undefined): {
|
|
14
|
+
placeholder?: "blur" | undefined;
|
|
15
|
+
blurDataURL?: string | undefined;
|
|
16
|
+
src: string;
|
|
17
|
+
alt: string;
|
|
18
|
+
width: number;
|
|
19
|
+
height: number;
|
|
20
|
+
} | null;
|
|
21
|
+
declare function getBackgroundPropsWithBlur(image: BlurImageSource | null | undefined): BlurPlaceholderStyle;
|
|
22
|
+
declare function getBlurPlaceholderStyle(image: BlurImageSource | null | undefined): BlurPlaceholderStyle;
|
|
23
|
+
declare function hasBlurData(image: BlurImageSource | null | undefined): boolean;
|
|
24
|
+
declare function getBulkBlurData(images: Array<BlurImageSource | null | undefined>): BlurData[];
|
|
25
|
+
/**
|
|
26
|
+
* Builds a responsive `srcset` descriptor for the full-resolution image URL.
|
|
27
|
+
* Never includes the low-resolution LQIP placeholder in `srcset` so browsers
|
|
28
|
+
* cannot accidentally select the blurred placeholder as the final image.
|
|
29
|
+
*/
|
|
30
|
+
declare function createSrcSetWithBlur(image: BlurImageSource | null | undefined): string;
|
|
31
|
+
/**
|
|
32
|
+
* Preloads the target image URL in `document.head` via `<link rel="preload" as="image">`.
|
|
33
|
+
* Does not set `imagesrcset` to the LQIP data URL and deduplicates existing preload links.
|
|
34
|
+
*/
|
|
35
|
+
declare function preloadImageWithBlur(image: BlurImageSource | null | undefined): void;
|
|
36
|
+
declare function extractImageUrls(image: BlurImageSource | null | undefined): string[];
|
|
37
|
+
/**
|
|
38
|
+
* Browser helper: Decodes a compact BlurHash string (e.g. "LEHV6nWB2yk8pyo0adR*.7kCMdnj")
|
|
39
|
+
* into a `data:image/png;base64,...` URL using an offscreen `<canvas>`.
|
|
40
|
+
*/
|
|
41
|
+
declare function decodeBlurHashToDataURL(hash: string, width?: number, height?: number, punch?: number): string | null;
|
|
42
|
+
|
|
43
|
+
export { type BlurPlaceholderStyle as B, getBlurData as a, getBlurPlaceholderStyle as b, createSrcSetWithBlur as c, decodeBlurHashToDataURL as d, extractImageUrls as e, getBulkBlurData as f, getBackgroundPropsWithBlur as g, getImagePropsWithBlur as h, hasBlurData as i, preloadImageWithBlur as p };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { c as BlurImageSource, a as BlurData } from './types-yShGNQnW.js';
|
|
2
|
+
|
|
3
|
+
interface BlurPlaceholderStyle {
|
|
4
|
+
backgroundColor: string;
|
|
5
|
+
backgroundImage?: string;
|
|
6
|
+
backgroundSize?: string;
|
|
7
|
+
backgroundPosition?: string;
|
|
8
|
+
backgroundRepeat?: string;
|
|
9
|
+
filter?: string;
|
|
10
|
+
transform?: string;
|
|
11
|
+
}
|
|
12
|
+
declare function getBlurData(image: BlurImageSource | null | undefined): BlurData;
|
|
13
|
+
declare function getImagePropsWithBlur(image: BlurImageSource | null | undefined): {
|
|
14
|
+
placeholder?: "blur" | undefined;
|
|
15
|
+
blurDataURL?: string | undefined;
|
|
16
|
+
src: string;
|
|
17
|
+
alt: string;
|
|
18
|
+
width: number;
|
|
19
|
+
height: number;
|
|
20
|
+
} | null;
|
|
21
|
+
declare function getBackgroundPropsWithBlur(image: BlurImageSource | null | undefined): BlurPlaceholderStyle;
|
|
22
|
+
declare function getBlurPlaceholderStyle(image: BlurImageSource | null | undefined): BlurPlaceholderStyle;
|
|
23
|
+
declare function hasBlurData(image: BlurImageSource | null | undefined): boolean;
|
|
24
|
+
declare function getBulkBlurData(images: Array<BlurImageSource | null | undefined>): BlurData[];
|
|
25
|
+
/**
|
|
26
|
+
* Builds a responsive `srcset` descriptor for the full-resolution image URL.
|
|
27
|
+
* Never includes the low-resolution LQIP placeholder in `srcset` so browsers
|
|
28
|
+
* cannot accidentally select the blurred placeholder as the final image.
|
|
29
|
+
*/
|
|
30
|
+
declare function createSrcSetWithBlur(image: BlurImageSource | null | undefined): string;
|
|
31
|
+
/**
|
|
32
|
+
* Preloads the target image URL in `document.head` via `<link rel="preload" as="image">`.
|
|
33
|
+
* Does not set `imagesrcset` to the LQIP data URL and deduplicates existing preload links.
|
|
34
|
+
*/
|
|
35
|
+
declare function preloadImageWithBlur(image: BlurImageSource | null | undefined): void;
|
|
36
|
+
declare function extractImageUrls(image: BlurImageSource | null | undefined): string[];
|
|
37
|
+
/**
|
|
38
|
+
* Browser helper: Decodes a compact BlurHash string (e.g. "LEHV6nWB2yk8pyo0adR*.7kCMdnj")
|
|
39
|
+
* into a `data:image/png;base64,...` URL using an offscreen `<canvas>`.
|
|
40
|
+
*/
|
|
41
|
+
declare function decodeBlurHashToDataURL(hash: string, width?: number, height?: number, punch?: number): string | null;
|
|
42
|
+
|
|
43
|
+
export { type BlurPlaceholderStyle as B, getBlurData as a, getBlurPlaceholderStyle as b, createSrcSetWithBlur as c, decodeBlurHashToDataURL as d, extractImageUrls as e, getBulkBlurData as f, getBackgroundPropsWithBlur as g, getImagePropsWithBlur as h, hasBlurData as i, preloadImageWithBlur as p };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { d as BlurManifestData, e as BlurManifestEntry, f as BlurPlaceholderType, i as BlurSize } from './types-yShGNQnW.js';
|
|
2
|
+
|
|
3
|
+
declare const DEFAULT_CDN_PATTERNS: RegExp[];
|
|
4
|
+
interface NormalizePathOptions {
|
|
5
|
+
cdnPatterns?: RegExp[];
|
|
6
|
+
stripPrefixes?: string[];
|
|
7
|
+
}
|
|
8
|
+
interface PathInfo {
|
|
9
|
+
normalizedPath: string;
|
|
10
|
+
folder?: string;
|
|
11
|
+
filename?: string;
|
|
12
|
+
extension?: string;
|
|
13
|
+
isExternal: boolean;
|
|
14
|
+
originalPath: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Project-agnostic image URL / path normalizer.
|
|
18
|
+
* Strips query parameters, leading `/public/`, relative `./`, and common CDN prefixes
|
|
19
|
+
* so manifest keys match consistently across local and CDN environments.
|
|
20
|
+
*/
|
|
21
|
+
declare function normalizeImagePath(imageSrc: string, options?: NormalizePathOptions): PathInfo;
|
|
22
|
+
|
|
23
|
+
interface BlurServiceOptions {
|
|
24
|
+
cacheSize?: number;
|
|
25
|
+
manifest?: BlurManifestData | Record<string, BlurManifestEntry>;
|
|
26
|
+
pathOptions?: NormalizePathOptions;
|
|
27
|
+
}
|
|
28
|
+
declare class BlurService {
|
|
29
|
+
private blurData;
|
|
30
|
+
private readonly cache;
|
|
31
|
+
private readonly pathOptions?;
|
|
32
|
+
private metadata;
|
|
33
|
+
private metrics;
|
|
34
|
+
constructor(options?: BlurServiceOptions);
|
|
35
|
+
/**
|
|
36
|
+
* Hydrate the service with a generated `blur-hashes.json` manifest or hash map.
|
|
37
|
+
*/
|
|
38
|
+
loadManifest(manifest: BlurManifestData | Record<string, BlurManifestEntry>): void;
|
|
39
|
+
/**
|
|
40
|
+
* Register or update a single image's blur data at runtime.
|
|
41
|
+
*/
|
|
42
|
+
registerEntry(imagePath: string, entry: BlurManifestEntry): void;
|
|
43
|
+
getDefaultPlaceholder(type?: BlurPlaceholderType): string;
|
|
44
|
+
private findBlurDataByPath;
|
|
45
|
+
getDominantColor(imageSrc: string): string;
|
|
46
|
+
getBlurDataURL(imageSrc: string, options?: {
|
|
47
|
+
type?: BlurPlaceholderType;
|
|
48
|
+
size?: BlurSize;
|
|
49
|
+
}): string;
|
|
50
|
+
getBulkBlurData(imageSrcs: string[], options?: {
|
|
51
|
+
type?: BlurPlaceholderType;
|
|
52
|
+
size?: BlurSize;
|
|
53
|
+
}): Record<string, string>;
|
|
54
|
+
getProgressiveData(imageSrc: string, size?: BlurSize): {
|
|
55
|
+
color: string;
|
|
56
|
+
blur: string;
|
|
57
|
+
hasBlur: boolean;
|
|
58
|
+
};
|
|
59
|
+
hasBlurHash(imageSrc: string): boolean;
|
|
60
|
+
getMetadata(): {
|
|
61
|
+
cacheSize: number;
|
|
62
|
+
metrics: {
|
|
63
|
+
cacheHitRate: string;
|
|
64
|
+
totalRequests: number;
|
|
65
|
+
cacheHits: number;
|
|
66
|
+
cacheMisses: number;
|
|
67
|
+
errors: number;
|
|
68
|
+
};
|
|
69
|
+
version: string;
|
|
70
|
+
generatedAt: string;
|
|
71
|
+
totalImages: number;
|
|
72
|
+
};
|
|
73
|
+
clearCache(): void;
|
|
74
|
+
getBlurHash(imageSrc: string): string | null;
|
|
75
|
+
preloadBlurData(imageSrcs: string[], size?: BlurSize): void;
|
|
76
|
+
fetchBlurDataForImage(imageSrc: string, type?: BlurPlaceholderType): Promise<string>;
|
|
77
|
+
getAvailableKeys(): string[];
|
|
78
|
+
findMatchingKeys(searchTerm: string): string[];
|
|
79
|
+
}
|
|
80
|
+
declare function createBlurService(options?: BlurServiceOptions): BlurService;
|
|
81
|
+
declare const blurService: BlurService;
|
|
82
|
+
|
|
83
|
+
export { BlurService as B, DEFAULT_CDN_PATTERNS as D, type NormalizePathOptions as N, type PathInfo as P, type BlurServiceOptions as a, blurService as b, createBlurService as c, normalizeImagePath as n };
|