visionary-image 1.0.0 → 1.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/README.md CHANGED
@@ -1,17 +1,17 @@
1
- # visionary-image
1
+ # Visionary Image
2
2
 
3
- ![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/visionary-ux/visionary-image/.github%2Fworkflows%2Fci-cd-workflow.yml?branch=master&style=flat-square)
3
+ React image component with built-in Blurhash placeholders for better UX and Core Web Vitals.
4
4
 
5
- > Visionary Image rapidly renders blurhash placeholders and boosts Core Web Vitals.
5
+ ![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/visionary-ux/visionary-image/.github%2Fworkflows%2Fci-cd-workflow.yml?branch=master&style=flat-square) ![npm package size (minzipped, via Bundlephobia)](https://img.shields.io/bundlephobia/minzip/visionary-image?style=flat-square&color=blue)
6
6
 
7
7
  **Features:**
8
8
 
9
- - Blurhash data encoded in the image URL via [visionary-url](https://github.com/visionary-ux/visionary-url) means zero latency image placeholders
10
- - Renders a true-to-size, responsive blurhash placeholder while the image loads, <u>eliminating Cumulative Layout Shift (CLS)</u>
11
- - Off-screen images are lazily loaded, reducing initial pageload size and request count, <u>improving Interaction to Next Paint (INP)</u>
9
+ - Blurhash data encoded in the image URL via [visionary-url](https://github.com/visionary-ux/visionary-url) for instant image placeholders
10
+ - Renders a true-to-size, responsive Blurhash placeholder while the image loads, eliminating Cumulative Layout Shift (CLS)
11
+ - Images below the fold are lazily loaded, reducing initial page load size and request count, optimizing Interaction to Next Paint (INP)
12
12
  - Framework-tested (Remix, Next.js, Vite) with support for both client and server-side rendering (SSR, SSG) environments
13
13
  - Additional features: Prevent dragging, user-selection of image
14
- - Improves Core Web Vitals scores (CLS, and often INP and LCP) which are a primary ranking signal in Google Search
14
+ - Improves Core Web Vitals scores which Google notes as an important ranking signal in Search.
15
15
 
16
16
  > "We highly recommend site owners achieve good Core Web Vitals for success with Search", [Google Search Central](https://developers.google.com/search/docs/appearance/core-web-vitals)
17
17
 
@@ -23,21 +23,19 @@ Install via npm, yarn, or pnpm.
23
23
  npm install --save visionary-image
24
24
  ```
25
25
 
26
- ## Getting Started
26
+ ## Usage
27
27
 
28
- To render a Visionary Image, first create a Visionary URL.
28
+ Create a Visionary image URL to get started.
29
29
 
30
30
  ### Creating a Visionary Image URL
31
31
 
32
- To create a Visionary URL from a public image URL see [Visionary URL Generator](#).
32
+ There are several ways to create a Visionary URL.
33
33
 
34
- To generate Visionary data from a local file, use the [Drag-and-Drop Blurhash Generator](#).
34
+ 1. Use the [Visionary URL Maker](https://visionary.cloud/url-maker) for public image URLs
35
+ 2. Use the [Drag & Drop Blurhash Generator](https://visionary.cloud/image-to-blurhash) for local image files
36
+ 3. Use [visionary-url](https://github.com/visionary-ux/visionary-url) to programmatically generate a URL
35
37
 
36
- - Generate a Visionary URL at [https://visionary.cloud/url-generator](https://visionary.cloud/url-generator)
37
- - Copy a URL from your [Visionary Dashboard](https://visionary.cloud/dashboard)
38
- - Use this [sample URL]()
39
-
40
- ### Render image component
38
+ ### Render Image
41
39
 
42
40
  ```tsx
43
41
  import { Image } from "visionary-image";
@@ -1,7 +1,7 @@
1
- (function(){"use strict";try{if(typeof document<"u"){var e=document.createElement("style");e.id="v7y-styles",e.appendChild(document.createTextNode('.v7y_7587:before{content:"";display:block;padding-top:var(--v-ar)}.v7y_f2b7{display:none}.v7y_b8c0{-webkit-user-drag:none;pointer-events:none}.v7y_e0dd{-webkit-user-select:none;user-select:none}')),document.head.appendChild(e)}}catch(n){console.error("vite-plugin-css-injected-by-js",n)}})();
2
- import { jsx as W, jsxs as yt } from "react/jsx-runtime";
3
- import { useRef as Y, useCallback as q, useState as wt, useEffect as xt, useLayoutEffect as Et, useMemo as It } from "react";
4
- function Rt(t) {
1
+ (function(){"use strict";try{if(typeof document<"u"){var e=document.createElement("style");e.id="v7y-styles",e.appendChild(document.createTextNode('.v7y_7587:before{content:"";display:block;padding-top:var(--v-ar)}.v7y_b8c0{-webkit-user-drag:none;pointer-events:none}.v7y_e0dd{-webkit-user-select:none;user-select:none}')),document.head.appendChild(e)}}catch(n){console.error("vite-plugin-css-injected-by-js",n)}})();
2
+ import { jsx as G, jsxs as vt } from "react/jsx-runtime";
3
+ import { useRef as Y, useCallback as q, useState as yt, useEffect as wt, useLayoutEffect as It, useMemo as xt } from "react";
4
+ function Et(t) {
5
5
  return t && t.__esModule && Object.prototype.hasOwnProperty.call(t, "default") ? t.default : t;
6
6
  }
7
7
  var et = { exports: {} };
@@ -12,8 +12,8 @@ var et = { exports: {} };
12
12
  */
13
13
  (function(t) {
14
14
  (function() {
15
- var n = {}.hasOwnProperty;
16
- function e() {
15
+ var e = {}.hasOwnProperty;
16
+ function n() {
17
17
  for (var r = "", s = 0; s < arguments.length; s++) {
18
18
  var a = arguments[s];
19
19
  a && (r = c(r, o(a)));
@@ -26,28 +26,28 @@ var et = { exports: {} };
26
26
  if (typeof r != "object")
27
27
  return "";
28
28
  if (Array.isArray(r))
29
- return e.apply(null, r);
29
+ return n.apply(null, r);
30
30
  if (r.toString !== Object.prototype.toString && !r.toString.toString().includes("[native code]"))
31
31
  return r.toString();
32
32
  var s = "";
33
33
  for (var a in r)
34
- n.call(r, a) && r[a] && (s = c(s, a));
34
+ e.call(r, a) && r[a] && (s = c(s, a));
35
35
  return s;
36
36
  }
37
37
  function c(r, s) {
38
38
  return s ? r ? r + " " + s : r + s : r;
39
39
  }
40
- t.exports ? (e.default = e, t.exports = e) : window.classNames = e;
40
+ t.exports ? (n.default = n, t.exports = n) : window.classNames = n;
41
41
  })();
42
42
  })(et);
43
- var At = et.exports;
44
- const K = /* @__PURE__ */ Rt(At), Nt = (t, { root: n, rootMargin: e, threshold: o } = {}, c = []) => {
43
+ var Rt = et.exports;
44
+ const At = /* @__PURE__ */ Et(Rt), St = (t, { root: e, rootMargin: n, threshold: o } = {}, c = []) => {
45
45
  const r = Y(null), s = Y(null);
46
46
  return q((i) => {
47
- r.current && s.current && (s.current.unobserve(r.current), s.current.disconnect(), s.current = null), i && (s.current = new IntersectionObserver(t, { root: n, rootMargin: e, threshold: o }), s.current.observe(i), r.current = i);
48
- }, [r, n, e, JSON.stringify(o), ...c]);
49
- }, Ot = ({ root: t, rootMargin: n, threshold: e, unobserveOnEnter: o, target: c, onEnter: r, onLeave: s, defaultInView: a } = {}, i = []) => {
50
- const [d, h] = wt({
47
+ r.current && s.current && (s.current.unobserve(r.current), s.current.disconnect(), s.current = null), i && (s.current = new IntersectionObserver(t, { root: e, rootMargin: n, threshold: o }), s.current.observe(i), r.current = i);
48
+ }, [r, e, n, JSON.stringify(o), ...c]);
49
+ }, Nt = ({ root: t, rootMargin: e, threshold: n, unobserveOnEnter: o, target: c, onEnter: r, onLeave: s, defaultInView: a } = {}, i = []) => {
50
+ const [d, h] = yt({
51
51
  inView: a || !1,
52
52
  entry: null,
53
53
  observer: null
@@ -58,14 +58,14 @@ const K = /* @__PURE__ */ Rt(At), Nt = (t, { root: n, rootMargin: e, threshold:
58
58
  entry: p,
59
59
  observer: l
60
60
  }), b && o && (l.unobserve(p.target), l.disconnect()), b ? r == null || r(p, l) : s == null || s(p, l);
61
- }, [r, s, o]), m = Nt(f, { root: t, rootMargin: n, threshold: e }, [o, ...i]);
62
- return xt(() => {
61
+ }, [r, s, o]), m = St(f, { root: t, rootMargin: e, threshold: n }, [o, ...i]);
62
+ return wt(() => {
63
63
  c != null && c.current && m(c.current);
64
64
  }, [c, m]), [m, d.inView, d.entry, d.observer];
65
65
  };
66
- var L = /* @__PURE__ */ ((t) => (t.AUTO = "auto", t.AVIF = "avif", t.JPEG = "jpeg", t.WEBP = "webp", t))(L || {}), y = /* @__PURE__ */ ((t) => (t.xs = "xs", t.sm = "sm", t.md = "md", t.lg = "lg", t.xl = "xl", t.xxl = "xxl", t["4k"] = "4k", t["5k"] = "5k", t.full = "full", t))(y || {}), C = /* @__PURE__ */ ((t) => (t.DEBUG = "debug", t.DOWNLOAD = "download", t))(C || {});
67
- const Ut = "https://link.visionary.cloud";
68
- L.AUTO, y.lg;
66
+ var $ = /* @__PURE__ */ ((t) => (t.AUTO = "auto", t.AVIF = "avif", t.JPEG = "jpeg", t.WEBP = "webp", t))($ || {}), y = /* @__PURE__ */ ((t) => (t.xs = "xs", t.sm = "sm", t.md = "md", t.lg = "lg", t.xl = "xl", t.xxl = "xxl", t["4k"] = "4k", t["5k"] = "5k", t.full = "full", t))(y || {}), P = /* @__PURE__ */ ((t) => (t.DEBUG = "debug", t.DOWNLOAD = "download", t))(P || {});
67
+ const Ot = "https://link.visionary.cloud";
68
+ $.AUTO, y.lg;
69
69
  const Z = {
70
70
  [y.xs]: 160,
71
71
  [y.sm]: 320,
@@ -78,61 +78,61 @@ const Z = {
78
78
  [y.full]: 0
79
79
  };
80
80
  Object.keys(Z).reduce(
81
- (t, n) => {
82
- const e = n, o = Z[e];
83
- return o && (t[o] = e), t;
81
+ (t, e) => {
82
+ const n = e, o = Z[n];
83
+ return o && (t[o] = n), t;
84
84
  },
85
85
  {}
86
86
  );
87
- const rt = "!";
88
- var St = Object.defineProperty, Tt = (t, n, e) => n in t ? St(t, n, { enumerable: !0, configurable: !0, writable: !0, value: e }) : t[n] = e, _t = (t, n, e) => Tt(t, typeof n != "symbol" ? n + "" : n, e), J = {}, j = {};
89
- Object.defineProperty(j, "__esModule", { value: !0 });
90
- function Ct(t) {
87
+ const nt = "!";
88
+ var Ut = Object.defineProperty, Tt = (t, e, n) => e in t ? Ut(t, e, { enumerable: !0, configurable: !0, writable: !0, value: n }) : t[e] = n, Dt = (t, e, n) => Tt(t, typeof e != "symbol" ? e + "" : e, n), J = {}, L = {};
89
+ Object.defineProperty(L, "__esModule", { value: !0 });
90
+ function Pt(t) {
91
91
  return String.fromCharCode(parseInt(t.slice(1), 16));
92
92
  }
93
- function Dt(t) {
94
- return btoa(encodeURIComponent(t).replace(/%[0-9A-F]{2}/g, Ct));
93
+ function _t(t) {
94
+ return btoa(encodeURIComponent(t).replace(/%[0-9A-F]{2}/g, Pt));
95
95
  }
96
- j.encode = Dt;
97
- function Pt(t) {
96
+ L.encode = _t;
97
+ function Ct(t) {
98
98
  return `%${`00${t.charCodeAt(0).toString(16)}`.slice(-2)}`;
99
99
  }
100
100
  function Vt(t) {
101
- return decodeURIComponent(Array.from(atob(t), Pt).join(""));
101
+ return decodeURIComponent(Array.from(atob(t), Ct).join(""));
102
102
  }
103
- j.decode = Vt;
103
+ L.decode = Vt;
104
104
  Object.defineProperty(J, "__esModule", { value: !0 });
105
- const ot = j;
105
+ const rt = L;
106
106
  function $t(t) {
107
- return ot.decode(t.replace(/\-/g, "+").replace(/_/g, "/"));
107
+ return rt.decode(t.replace(/\-/g, "+").replace(/_/g, "/"));
108
108
  }
109
109
  var Lt = J.decode = $t;
110
110
  function jt(t) {
111
- return ot.encode(t).replace(/\//g, "_").replace(/\+/g, "-").replace(/=+$/, "");
111
+ return rt.encode(t).replace(/\//g, "_").replace(/\+/g, "-").replace(/=+$/, "");
112
112
  }
113
113
  var kt = J.encode = jt;
114
- const zt = (t) => t.filter(Boolean), Q = (t) => {
114
+ const zt = (t) => t.filter(Boolean), K = (t) => {
115
115
  try {
116
116
  return new URL(t);
117
117
  } catch {
118
118
  return null;
119
119
  }
120
- }, k = (t = "") => /^[A-Za-z0-9_-]*$/.test(t), Bt = (t) => {
121
- const { altText: n, bcc: e, blurhash: o, blurhashX: c, blurhashY: r, sourceHeight: s, sourceWidth: a, url: i } = t;
120
+ }, j = (t = "") => /^[A-Za-z0-9_-]*$/.test(t), Bt = (t) => {
121
+ const { altText: e, bcc: n, blurhash: o, blurhashX: c, blurhashY: r, sourceHeight: s, sourceWidth: a, url: i } = t;
122
122
  if (!i || !a || !s)
123
123
  return new Error("Cannot construct visionary code: missing required url/width/height");
124
124
  const d = [i, a, s];
125
- return !e || (d.push(e), !o || !c || !r) || (d.push(o, c, r), n && n.length && d.push(n)), Mt(d);
126
- }, Mt = (t) => kt(t.join(rt)), st = (t) => {
125
+ return !n || (d.push(n), !o || !c || !r) || (d.push(o, c, r), e && e.length && d.push(e)), Mt(d);
126
+ }, Mt = (t) => kt(t.join(nt)), ot = (t) => {
127
127
  if (typeof t != "string")
128
128
  return null;
129
- const n = t.trim();
130
- if (!n.length || !k(n))
129
+ const e = t.trim();
130
+ if (!e.length || !j(e))
131
131
  return null;
132
- const e = Lt(n);
133
- if (!e)
132
+ const n = Lt(e);
133
+ if (!n)
134
134
  return null;
135
- const o = e.split(rt);
135
+ const o = n.split(nt);
136
136
  if (o.length < 3)
137
137
  return null;
138
138
  const [c, r, s, a, i, d, h, f] = o, m = c.trim();
@@ -159,25 +159,25 @@ const zt = (t) => t.filter(Boolean), Q = (t) => {
159
159
  };
160
160
  class Gt extends Error {
161
161
  constructor() {
162
- super(...arguments), _t(this, "message", "invalid endpoint URL (does it contain http/https?)");
162
+ super(...arguments), Dt(this, "message", "invalid endpoint URL (does it contain http/https?)");
163
163
  }
164
164
  }
165
- const Wt = (t) => t === C.DEBUG, Ft = (t) => t === C.DOWNLOAD, Ht = (t) => Object.values(L).includes(t), ct = (t) => Object.values(y).includes(t), Xt = (t = []) => {
166
- const n = {};
167
- for (const e of t)
168
- ct(e) ? n.size = y[e] : Wt(e) ? n.debug = !0 : Ft(e) ? n.download = !0 : Ht(e) && (n.format = e);
169
- return n;
165
+ const Wt = (t) => t === P.DEBUG, Ft = (t) => t === P.DOWNLOAD, Ht = (t) => Object.values($).includes(t), st = (t) => Object.values(y).includes(t), Xt = (t = []) => {
166
+ const e = {};
167
+ for (const n of t)
168
+ st(n) ? e.size = y[n] : Wt(n) ? e.debug = !0 : Ft(n) ? e.download = !0 : Ht(n) && (e.format = n);
169
+ return e;
170
170
  }, Yt = (t) => {
171
171
  if (!t || typeof t != "object")
172
172
  return null;
173
- const n = [];
174
- return t.debug && n.push(C.DEBUG), t.download && n.push(C.DOWNLOAD), t.format && t.format !== L.AUTO && n.push(t.format), t.size && ct(t.size) && n.push(t.size), n.length ? n.sort().join(",") : null;
173
+ const e = [];
174
+ return t.debug && e.push(P.DEBUG), t.download && e.push(P.DOWNLOAD), t.format && t.format !== $.AUTO && e.push(t.format), t.size && st(t.size) && e.push(t.size), e.length ? e.sort().join(",") : null;
175
175
  }, Zt = (t) => {
176
- if (k(t)) {
177
- const n = st(t);
178
- if (n)
176
+ if (j(t)) {
177
+ const e = ot(t);
178
+ if (e)
179
179
  return {
180
- fields: n,
180
+ fields: e,
181
181
  options: {}
182
182
  };
183
183
  }
@@ -185,14 +185,14 @@ const Wt = (t) => t === C.DEBUG, Ft = (t) => t === C.DOWNLOAD, Ht = (t) => Objec
185
185
  }, qt = (t) => {
186
186
  if (!t)
187
187
  return null;
188
- const n = t.trim();
189
- if (!n)
188
+ const e = t.trim();
189
+ if (!e)
190
190
  return null;
191
191
  try {
192
- const e = Kt(n);
193
- if (!e)
192
+ const n = Kt(e);
193
+ if (!n)
194
194
  return null;
195
- const { code: o, optionTokens: c } = e, r = st(o);
195
+ const { code: o, optionTokens: c } = n, r = ot(o);
196
196
  if (!r)
197
197
  return null;
198
198
  const s = Xt(c);
@@ -200,38 +200,38 @@ const Wt = (t) => t === C.DEBUG, Ft = (t) => t === C.DOWNLOAD, Ht = (t) => Objec
200
200
  fields: r,
201
201
  options: s
202
202
  };
203
- } catch (e) {
204
- e instanceof Error ? console.error(`Error parsing URL: ${e.message}`, e) : console.error("uncaught error", e);
203
+ } catch (n) {
204
+ n instanceof Error ? console.error(`Error parsing URL: ${n.message}`, n) : console.error("uncaught error", n);
205
205
  }
206
206
  return null;
207
- }, Jt = (t, n) => {
208
- const e = Bt(t);
209
- if (e instanceof Error)
207
+ }, Jt = (t, e) => {
208
+ const n = Bt(t);
209
+ if (n instanceof Error)
210
210
  return null;
211
211
  let o = null;
212
- if (n != null && n.endpoint && (o = Q(n == null ? void 0 : n.endpoint), !o))
212
+ if (e != null && e.endpoint && (o = K(e == null ? void 0 : e.endpoint), !o))
213
213
  throw new Gt(
214
214
  "Cannot construct URL: bad endpoint. Ensure endpoint starts with http:// or https://"
215
215
  );
216
- o || (o = Q(Ut));
217
- const c = [o.origin, "image", e], r = n ? Yt(n) : null;
218
- return r && c.push(r), n != null && n.filename ? c.push(n.filename) : c.push("image.jpg"), c.join("/");
216
+ o || (o = K(Ot));
217
+ const c = [o.origin, "image", n], r = e ? Yt(e) : null;
218
+ return r && c.push(r), e != null && e.filename ? c.push(e.filename) : c.push("image.jpg"), c.join("/");
219
219
  }, Kt = (t) => {
220
220
  try {
221
- const n = new URL(t), e = zt(n.pathname.split("/"));
222
- if (e[0] !== "image" || ![3, 4].includes(e.length))
221
+ const e = new URL(t), n = zt(e.pathname.split("/"));
222
+ if (n[0] !== "image" || ![3, 4].includes(n.length))
223
223
  throw new Error("Unrecognized URL");
224
- const o = e[1].trim();
225
- if (!o.length || !k(o))
224
+ const o = n[1].trim();
225
+ if (!o.length || !j(o))
226
226
  throw new Error("URL is not formatted as base64url");
227
- if (e.length === 4) {
228
- const c = e[2].split(",");
227
+ if (n.length === 4) {
228
+ const c = n[2].split(",");
229
229
  return {
230
230
  code: o,
231
231
  optionTokens: c
232
232
  };
233
233
  }
234
- if (e.length === 3)
234
+ if (n.length === 3)
235
235
  return {
236
236
  code: o,
237
237
  optionTokens: []
@@ -240,214 +240,218 @@ const Wt = (t) => t === C.DEBUG, Ft = (t) => t === C.DOWNLOAD, Ht = (t) => Objec
240
240
  return null;
241
241
  }
242
242
  return null;
243
- }, it = 0.7, at = 1, O = 24, Qt = y.lg, lt = process.env.NODE_ENV, tn = lt === "development", ut = typeof document > "u", nn = lt === "test", en = ut ? () => {
244
- } : Et, rn = "<visionary>", N = (t, ...n) => {
245
- !console || typeof console.log != "function" || console.log(`${rn} ${t}`, ...n);
246
- }, on = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~", V = (t, n, e) => {
243
+ }, ct = 0.7, it = 1, N = 24, Qt = y.lg, at = process.env.NODE_ENV, te = at === "development", lt = typeof document > "u", ee = at === "test", ne = lt ? () => {
244
+ } : It, re = "<visionary>", S = (t, ...e) => {
245
+ !console || typeof console.log != "function" || console.log(`${re} ${t}`, ...e);
246
+ }, oe = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~", C = (t, e, n) => {
247
247
  let o = 0;
248
- for (; n < e; )
249
- o *= 83, o += on.indexOf(t[n++]);
248
+ for (; e < n; )
249
+ o *= 83, o += oe.indexOf(t[e++]);
250
250
  return o;
251
- }, dt = Math.pow, D = Math.PI, sn = D * 2, ft = 3294.6, pt = 269.025, cn = (t) => t > 10.31475 ? dt(t / pt + 0.052132, 2.4) : t / ft, F = (t) => ~~(t > 1227e-8 ? pt * dt(t, 0.416666) - 13.025 : t * ft + 1), _ = (t) => (t < 0 ? -1 : 1) * t * t, tt = (t) => {
252
- for (t += D / 2; t > D; )
253
- t -= sn;
254
- const n = 1.27323954 * t - 0.405284735 * _(t);
255
- return 0.225 * (_(n) - n) + n;
251
+ }, ut = Math.pow, _ = Math.PI, se = _ * 2, dt = 3294.6, ft = 269.025, ce = (t) => t > 10.31475 ? ut(t / ft + 0.052132, 2.4) : t / dt, W = (t) => ~~(t > 1227e-8 ? ft * ut(t, 0.416666) - 13.025 : t * dt + 1), D = (t) => (t < 0 ? -1 : 1) * t * t, Q = (t) => {
252
+ for (t += _ / 2; t > _; )
253
+ t -= se;
254
+ const e = 1.27323954 * t - 0.405284735 * D(t);
255
+ return 0.225 * (D(e) - e) + e;
256
256
  };
257
- function an(t) {
258
- const n = V(t, 2, 6);
259
- return [n >> 16, n >> 8 & 255, n & 255];
257
+ function ie(t) {
258
+ const e = C(t, 2, 6);
259
+ return [e >> 16, e >> 8 & 255, e & 255];
260
260
  }
261
- function ln(t, n, e, o) {
262
- const c = V(t, 0, 1), r = c % 9 + 1, s = ~~(c / 9) + 1, a = r * s;
263
- let i = 0, d = 0, h = 0, f = 0, m = 0, p = 0, l = 0, v = 0, b = 0, g = 0, w = 0, E = 0, u = 0, I = 0;
264
- const U = (V(t, 1, 2) + 1) / 13446 * (o | 1), x = new Float64Array(a * 3), z = an(t);
261
+ function ae(t, e, n, o) {
262
+ const c = C(t, 0, 1), r = c % 9 + 1, s = ~~(c / 9) + 1, a = r * s;
263
+ let i = 0, d = 0, h = 0, f = 0, m = 0, p = 0, l = 0, v = 0, b = 0, g = 0, w = 0, x = 0, u = 0, E = 0;
264
+ const O = (C(t, 1, 2) + 1) / 13446 * (o | 1), I = new Float64Array(a * 3), k = ie(t);
265
265
  for (i = 0; i < 3; i++)
266
- x[i] = cn(z[i]);
266
+ I[i] = ce(k[i]);
267
267
  for (i = 1; i < a; i++)
268
- I = V(t, 4 + i * 2, 6 + i * 2), x[i * 3] = _(~~(I / (19 * 19)) - 9) * U, x[i * 3 + 1] = _(~~(I / 19) % 19 - 9) * U, x[i * 3 + 2] = _(I % 19 - 9) * U;
269
- const S = n * 4, R = new Uint8ClampedArray(S * e);
270
- for (f = 0; f < e; f++)
271
- for (E = D * f / e, h = 0; h < n; h++) {
272
- for (m = 0, p = 0, l = 0, u = D * h / n, d = 0; d < s; d++)
273
- for (b = tt(E * d), i = 0; i < r; i++)
274
- v = tt(u * i) * b, g = (i + d * r) * 3, m += x[g] * v, p += x[g + 1] * v, l += x[g + 2] * v;
275
- w = 4 * h + f * S, R[w] = F(m), R[w + 1] = F(p), R[w + 2] = F(l), R[w + 3] = 255;
268
+ E = C(t, 4 + i * 2, 6 + i * 2), I[i * 3] = D(~~(E / (19 * 19)) - 9) * O, I[i * 3 + 1] = D(~~(E / 19) % 19 - 9) * O, I[i * 3 + 2] = D(E % 19 - 9) * O;
269
+ const U = e * 4, R = new Uint8ClampedArray(U * n);
270
+ for (f = 0; f < n; f++)
271
+ for (x = _ * f / n, h = 0; h < e; h++) {
272
+ for (m = 0, p = 0, l = 0, u = _ * h / e, d = 0; d < s; d++)
273
+ for (b = Q(x * d), i = 0; i < r; i++)
274
+ v = Q(u * i) * b, g = (i + d * r) * 3, m += I[g] * v, p += I[g + 1] * v, l += I[g + 2] * v;
275
+ w = 4 * h + f * U, R[w] = W(m), R[w + 1] = W(p), R[w + 2] = W(l), R[w + 3] = 255;
276
276
  }
277
277
  return R;
278
278
  }
279
- const $ = (t) => {
279
+ const V = (t) => {
280
280
  try {
281
281
  return new URL(t);
282
282
  } catch {
283
283
  return null;
284
284
  }
285
- }, un = (t, n) => `rgba(${t.r},${t.g},${t.b},${n})`, H = (t) => tn || nn ? {
285
+ }, le = (t, e) => `rgba(${t.r},${t.g},${t.b},${e})`, F = (t) => te || ee ? {
286
286
  "data-testid": t
287
- } : {}, dn = (t, n) => n ? {
287
+ } : {}, ue = (t, e) => e ? {
288
288
  "data-fileid": t
289
- } : {}, nt = (t, n) => Math.min(t, n), fn = (t) => {
290
- let n = t.replace(/^#/, "");
291
- if (n.length !== 3 && n.length !== 6)
289
+ } : {}, tt = (t, e) => Math.min(t, e), de = (t) => {
290
+ let e = t.replace(/^#/, "");
291
+ if (e.length !== 3 && e.length !== 6)
292
292
  return null;
293
- n.length === 3 && (n = `${n[0]}${n[0]}${n[1]}${n[1]}${n[2]}${n[2]}`);
294
- const e = parseInt(n.slice(0, 2), 16), o = parseInt(n.slice(2, 4), 16);
295
- return { b: parseInt(n.slice(4, 6), 16), g: o, r: e };
296
- }, A = (t, n = 0) => {
297
- const e = Math.pow(10, n);
298
- return Math.round(t * e) / e;
299
- }, pn = (t, n) => {
300
- const e = $(t), o = $(n);
301
- return !e || !o ? t : (o.pathname = e.pathname, o.search = e.search, o.toString());
302
- }, hn = (t, n = {}, e = it, o = at) => {
293
+ e.length === 3 && (e = `${e[0]}${e[0]}${e[1]}${e[1]}${e[2]}${e[2]}`);
294
+ const n = parseInt(e.slice(0, 2), 16), o = parseInt(e.slice(2, 4), 16);
295
+ return { b: parseInt(e.slice(4, 6), 16), g: o, r: n };
296
+ }, A = (t, e = 0) => {
297
+ const n = Math.pow(10, e);
298
+ return Math.round(t * n) / n;
299
+ }, fe = (t, e) => {
300
+ const n = V(t), o = V(e);
301
+ return !n || !o ? t : (o.pathname = n.pathname, o.search = n.search, o.toString());
302
+ }, pe = (t, e = {}, n = ct, o = it) => {
303
303
  if (!t)
304
304
  return null;
305
305
  try {
306
- n.debug && N("input imageSrc:", t);
306
+ e.debug && S("input imageSrc:", t);
307
307
  const c = Zt(t);
308
- if (n.debug && N("Visionary URL data: ", c), !c)
308
+ if (e.debug && S("Visionary URL data: ", c), !c)
309
309
  throw new Error("Could not parse Visionary URL");
310
310
  const { fields: r, options: s } = c;
311
311
  if (r.sourceWidth < 1 || r.sourceHeight < 1)
312
312
  throw new Error("Invalid image dimensions");
313
- const a = r.sourceWidth / r.sourceHeight, i = (n == null ? void 0 : n.size) ?? s.size ?? Qt, d = Z[i];
313
+ const a = r.sourceWidth / r.sourceHeight, i = (e == null ? void 0 : e.size) ?? s.size ?? Qt, d = Z[i];
314
314
  let h = 0, f = 0, m = 0;
315
315
  if (a >= 1) {
316
- f = h = nt(d, r.sourceWidth);
316
+ f = h = tt(d, r.sourceWidth);
317
317
  const g = A(f / a);
318
318
  m = A(f / g, 6);
319
319
  } else
320
- h = nt(d, r.sourceHeight), f = A(h * a), m = A(f / h, 6);
320
+ h = tt(d, r.sourceHeight), f = A(h * a), m = A(f / h, 6);
321
321
  const p = A(100 / m, 6), l = {
322
322
  ...r,
323
323
  arPaddingTop: `${p}%`,
324
324
  aspectRatio: m,
325
325
  maxWidth: f,
326
326
  src: t
327
- }, v = $(r.url);
327
+ }, v = V(r.url);
328
328
  if (v)
329
329
  l.src = v.toString();
330
- else if (!$(t) && k(r.url)) {
330
+ else if (!V(t) && j(r.url)) {
331
331
  const g = Jt(r, {
332
- endpoint: n.endpoint,
332
+ endpoint: e.endpoint,
333
333
  size: i
334
334
  });
335
335
  g && (l.src = g);
336
336
  }
337
- n.endpoint && (l.src = pn(l.src, n.endpoint));
338
- const b = r.bcc ? fn(r.bcc) : null;
339
- if (b && (l.backgroundColor = un(b, e)), !ut && r.blurhash && !n.disableBlurLayer) {
340
- const g = performance.now(), w = ln(r.blurhash, O, O, o), E = performance.now() - g;
341
- n.debug && N(`Blurhash decode time: ${A(E, 1)} ms`), w && (l.pixels = w);
337
+ e.endpoint && (l.src = fe(l.src, e.endpoint));
338
+ const b = r.bcc ? de(r.bcc) : null;
339
+ if (b && (l.backgroundColor = le(b, n)), !lt && r.blurhash && !e.disableBlurLayer) {
340
+ const g = performance.now(), w = ae(r.blurhash, N, N, o), x = performance.now() - g;
341
+ e.debug && S(`Blurhash decode time: ${A(x, 1)} ms`), w && (l.pixels = w);
342
342
  }
343
- return n.debug && N("Visionary Image state: ", l), l;
343
+ return e.debug && S("Visionary Image state: ", l), l;
344
344
  } catch {
345
345
  return null;
346
346
  }
347
- }, ht = {
347
+ }, pt = {
348
348
  bottom: 0,
349
349
  left: 0,
350
350
  position: "absolute",
351
351
  right: 0,
352
352
  top: 0
353
- }, mn = {
354
- ...ht,
353
+ }, he = {
354
+ ...pt,
355
355
  maxHeight: "100%",
356
356
  maxWidth: "100%"
357
- }, gn = {
358
- ...ht,
357
+ }, me = {
358
+ ...pt,
359
359
  height: "100%",
360
360
  width: "100%"
361
- }, X = {
361
+ }, ge = (t) => {
362
+ const e = {
363
+ ...he
364
+ };
365
+ return t && (e.display = "none"), e;
366
+ }, H = {
362
367
  CANVAS: "canvas",
363
368
  CONTAINER: "container",
364
369
  IMAGE: "image"
365
- }, bn = "v7y_7587", vn = "v7y_f2b7", yn = "v7y_b8c0", wn = "v7y_e0dd", P = {
366
- container: bn,
367
- hidden: vn,
368
- preventDrag: yn,
369
- preventSelection: wn
370
- }, An = ({
370
+ }, be = "v7y_7587", ve = "v7y_b8c0", ye = "v7y_e0dd", X = {
371
+ container: be,
372
+ preventDrag: ve,
373
+ preventSelection: ye
374
+ }, Re = ({
371
375
  alt: t,
372
- bgColorAlpha: n = it,
373
- className: e,
376
+ bgColorAlpha: e = ct,
377
+ className: n,
374
378
  debug: o = !1,
375
379
  disableBlurLayer: c = !1,
376
380
  disableImageLayer: r = !1,
377
381
  endpoint: s,
378
382
  height: a,
379
- hideImageLayer: i,
383
+ hideImageLayer: i = !1,
380
384
  lazy: d = !0,
381
385
  onClick: h,
382
386
  onError: f,
383
387
  onLoad: m,
384
388
  preventDrag: p = !1,
385
389
  preventSelection: l = !1,
386
- punch: v = at,
390
+ punch: v = it,
387
391
  size: b,
388
392
  src: g,
389
393
  style: w,
390
- width: E
394
+ width: x
391
395
  }) => {
392
- const u = It(() => hn(g, {
396
+ const u = xt(() => pe(g, {
393
397
  debug: o,
394
398
  disableBlurLayer: c,
395
399
  endpoint: s,
396
400
  size: b
397
- }, n, v), [n, o, c, s, v, b, g]), I = Y(null), [U, x] = Ot();
398
- en(
401
+ }, e, v), [e, o, c, s, v, b, g]), E = Y(null), [O, I] = Nt();
402
+ ne(
399
403
  function() {
400
- if (!I.current || !(u != null && u.blurhash))
404
+ if (!E.current || !(u != null && u.blurhash))
401
405
  return;
402
406
  if (!u.pixels) {
403
407
  console.warn("No pixel data passed in, skipping");
404
408
  return;
405
409
  }
406
- const bt = performance.now(), M = I.current.getContext("2d");
407
- if (!M) {
410
+ const gt = performance.now(), B = E.current.getContext("2d");
411
+ if (!B) {
408
412
  console.warn("Cannot access canvasRenderingContext, skipping");
409
413
  return;
410
414
  }
411
- const G = M.createImageData(O, O);
412
- if (!G) {
415
+ const M = B.createImageData(N, N);
416
+ if (!M) {
413
417
  console.warn("Could not create ImageData on CanvasRenderingContext");
414
418
  return;
415
419
  }
416
- if (G.data.set(u.pixels), M.putImageData(G, 0, 0), o) {
417
- const vt = performance.now() - bt;
418
- N(`Canvas render time: ${A(vt, 1)} ms`);
420
+ if (M.data.set(u.pixels), B.putImageData(M, 0, 0), o) {
421
+ const bt = performance.now() - gt;
422
+ S(`Canvas render time: ${A(bt, 1)} ms`);
419
423
  }
420
424
  },
421
425
  [u == null ? void 0 : u.blurhash, u == null ? void 0 : u.pixels, o]
422
426
  );
423
- const z = q(
427
+ const k = q(
424
428
  (T) => {
425
- o && N("Image load error ", T), f && f();
429
+ o && S("Image load error ", T), f && f();
426
430
  },
427
431
  [o, f]
428
- ), S = {
432
+ ), U = {
429
433
  alt: t,
430
- loading: !d || x ? "eager" : "lazy",
434
+ loading: !d || I ? "eager" : "lazy",
431
435
  onLoad: m
432
436
  };
433
437
  if (!u) {
434
- o && N("Not a Visionary URL, rendering fallback <img />");
438
+ o && S("Not a Visionary URL, rendering fallback <img />");
435
439
  const T = {
436
- ...S,
437
- className: e,
440
+ ...U,
441
+ className: n,
438
442
  height: a,
439
443
  onClick: h,
440
444
  onError: f,
441
445
  src: g,
442
446
  style: w,
443
- width: E
447
+ width: x
444
448
  };
445
- return /* @__PURE__ */ W("img", { ...T });
449
+ return /* @__PURE__ */ G("img", { ...T });
446
450
  }
447
- const R = K(P.container, e, {
448
- [P.preventDrag]: !!p,
449
- [P.preventSelection]: !!l
450
- }), B = {
451
+ const R = At(X.container, n, {
452
+ [X.preventDrag]: !!p,
453
+ [X.preventSelection]: !!l
454
+ }), z = {
451
455
  ...{
452
456
  "--v-ar": u.arPaddingTop
453
457
  },
@@ -458,42 +462,39 @@ const $ = (t) => {
458
462
  width: "100%",
459
463
  ...w
460
464
  };
461
- E && (B.width = E), a && (B.height = a);
462
- const mt = {
465
+ x && (z.width = x), a && (z.height = a);
466
+ const ht = {
463
467
  onClick: h
464
- }, gt = K({
465
- [P.hidden]: !!i
466
- });
467
- return /* @__PURE__ */ yt(
468
+ }, mt = ge(i);
469
+ return /* @__PURE__ */ vt(
468
470
  "div",
469
471
  {
470
472
  className: R,
471
- ref: U,
472
- style: B,
473
- ...dn(u.url, o),
474
- ...H(X.CONTAINER),
475
- ...mt,
473
+ ref: O,
474
+ style: z,
475
+ ...ue(u.url, o),
476
+ ...F(H.CONTAINER),
477
+ ...ht,
476
478
  children: [
477
- !c && /* @__PURE__ */ W(
479
+ !c && /* @__PURE__ */ G(
478
480
  "canvas",
479
481
  {
480
- height: O,
481
- ref: I,
482
- style: gn,
483
- width: O,
484
- ...H(X.CANVAS)
482
+ height: N,
483
+ ref: E,
484
+ style: me,
485
+ width: N,
486
+ ...F(H.CANVAS)
485
487
  }
486
488
  ),
487
489
  !r && // eslint-disable-next-line jsx-a11y/alt-text -- `sharedImgProps` includes alt tag
488
- /* @__PURE__ */ W(
490
+ /* @__PURE__ */ G(
489
491
  "img",
490
492
  {
491
- ...S,
492
- ...H(X.IMAGE),
493
- className: gt,
494
- onError: z,
493
+ ...U,
494
+ ...F(H.IMAGE),
495
+ onError: k,
495
496
  src: u.src,
496
- style: mn
497
+ style: mt
497
498
  }
498
499
  )
499
500
  ]
@@ -501,5 +502,5 @@ const $ = (t) => {
501
502
  );
502
503
  };
503
504
  export {
504
- An as Image
505
+ Re as Image
505
506
  };
@@ -1,6 +1,6 @@
1
- (function(){"use strict";try{if(typeof document<"u"){var e=document.createElement("style");e.id="v7y-styles",e.appendChild(document.createTextNode('.v7y_7587:before{content:"";display:block;padding-top:var(--v-ar)}.v7y_f2b7{display:none}.v7y_b8c0{-webkit-user-drag:none;pointer-events:none}.v7y_e0dd{-webkit-user-select:none;user-select:none}')),document.head.appendChild(e)}}catch(n){console.error("vite-plugin-css-injected-by-js",n)}})();
2
- (function(E,R){typeof exports=="object"&&typeof module<"u"?R(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],R):(E=typeof globalThis<"u"?globalThis:E||self,R(E.VisionaryImage={},E.jsxRuntime,E.React))})(this,function(E,R,x){"use strict";function me(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var K={exports:{}};/*!
1
+ (function(){"use strict";try{if(typeof document<"u"){var e=document.createElement("style");e.id="v7y-styles",e.appendChild(document.createTextNode('.v7y_7587:before{content:"";display:block;padding-top:var(--v-ar)}.v7y_b8c0{-webkit-user-drag:none;pointer-events:none}.v7y_e0dd{-webkit-user-select:none;user-select:none}')),document.head.appendChild(e)}}catch(n){console.error("vite-plugin-css-injected-by-js",n)}})();
2
+ (function(E,R){typeof exports=="object"&&typeof module<"u"?R(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],R):(E=typeof globalThis<"u"?globalThis:E||self,R(E.VisionaryImage={},E.jsxRuntime,E.React))})(this,function(E,R,I){"use strict";function ht(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var K={exports:{}};/*!
3
3
  Copyright (c) 2018 Jed Watson.
4
4
  Licensed under the MIT License (MIT), see
5
5
  http://jedwatson.github.io/classnames
6
- */(function(e){(function(){var t={}.hasOwnProperty;function n(){for(var r="",s=0;s<arguments.length;s++){var a=arguments[s];a&&(r=i(r,o(a)))}return r}function o(r){if(typeof r=="string"||typeof r=="number")return r;if(typeof r!="object")return"";if(Array.isArray(r))return n.apply(null,r);if(r.toString!==Object.prototype.toString&&!r.toString.toString().includes("[native code]"))return r.toString();var s="";for(var a in r)t.call(r,a)&&r[a]&&(s=i(s,a));return s}function i(r,s){return s?r?r+" "+s:r+s:r}e.exports?(n.default=n,e.exports=n):window.classNames=n})()})(K);var ge=K.exports;const Q=me(ge),be=(e,{root:t,rootMargin:n,threshold:o}={},i=[])=>{const r=x.useRef(null),s=x.useRef(null);return x.useCallback(c=>{r.current&&s.current&&(s.current.unobserve(r.current),s.current.disconnect(),s.current=null),c&&(s.current=new IntersectionObserver(e,{root:t,rootMargin:n,threshold:o}),s.current.observe(c),r.current=c)},[r,t,n,JSON.stringify(o),...i])},ve=({root:e,rootMargin:t,threshold:n,unobserveOnEnter:o,target:i,onEnter:r,onLeave:s,defaultInView:a}={},c=[])=>{const[d,h]=x.useState({inView:a||!1,entry:null,observer:null}),f=x.useCallback(([p],l)=>{const b=l.thresholds.some(g=>p.intersectionRatio>=g)&&p.isIntersecting;h({inView:b,entry:p,observer:l}),b&&o&&(l.unobserve(p.target),l.disconnect()),b?r==null||r(p,l):s==null||s(p,l)},[r,s,o]),m=be(f,{root:e,rootMargin:t,threshold:n},[o,...c]);return x.useEffect(()=>{i!=null&&i.current&&m(i.current)},[i,m]),[m,d.inView,d.entry,d.observer]};var $=(e=>(e.AUTO="auto",e.AVIF="avif",e.JPEG="jpeg",e.WEBP="webp",e))($||{}),y=(e=>(e.xs="xs",e.sm="sm",e.md="md",e.lg="lg",e.xl="xl",e.xxl="xxl",e["4k"]="4k",e["5k"]="5k",e.full="full",e))(y||{}),C=(e=>(e.DEBUG="debug",e.DOWNLOAD="download",e))(C||{});const ye="https://link.visionary.cloud";$.AUTO,y.lg;const G={[y.xs]:160,[y.sm]:320,[y.md]:640,[y.lg]:1280,[y.xl]:1920,[y.xxl]:2560,[y["4k"]]:3840,[y["5k"]]:5120,[y.full]:0};Object.keys(G).reduce((e,t)=>{const n=t,o=G[n];return o&&(e[o]=n),e},{});const ee="!";var we=Object.defineProperty,xe=(e,t,n)=>t in e?we(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,Ie=(e,t,n)=>xe(e,typeof t!="symbol"?t+"":t,n),W={},L={};Object.defineProperty(L,"__esModule",{value:!0});function Ee(e){return String.fromCharCode(parseInt(e.slice(1),16))}function Re(e){return btoa(encodeURIComponent(e).replace(/%[0-9A-F]{2}/g,Ee))}L.encode=Re;function Ae(e){return`%${`00${e.charCodeAt(0).toString(16)}`.slice(-2)}`}function Se(e){return decodeURIComponent(Array.from(atob(e),Ae).join(""))}L.decode=Se,Object.defineProperty(W,"__esModule",{value:!0});const te=L;function Te(e){return te.decode(e.replace(/\-/g,"+").replace(/_/g,"/"))}var Ne=W.decode=Te;function Oe(e){return te.encode(e).replace(/\//g,"_").replace(/\+/g,"-").replace(/=+$/,"")}var Ue=W.encode=Oe;const Ce=e=>e.filter(Boolean),ne=e=>{try{return new URL(e)}catch{return null}},k=(e="")=>/^[A-Za-z0-9_-]*$/.test(e),De=e=>{const{altText:t,bcc:n,blurhash:o,blurhashX:i,blurhashY:r,sourceHeight:s,sourceWidth:a,url:c}=e;if(!c||!a||!s)return new Error("Cannot construct visionary code: missing required url/width/height");const d=[c,a,s];return!n||(d.push(n),!o||!i||!r)||(d.push(o,i,r),t&&t.length&&d.push(t)),Pe(d)},Pe=e=>Ue(e.join(ee)),re=e=>{if(typeof e!="string")return null;const t=e.trim();if(!t.length||!k(t))return null;const n=Ne(t);if(!n)return null;const o=n.split(ee);if(o.length<3)return null;const[i,r,s,a,c,d,h,f]=o,m=i.trim();if(!m.length)return console.error("Cannot parse code, empty file id"),null;const p=Number(r.trim()),l=Number(s.trim());if(isNaN(p)||isNaN(l)||!p||!l)return console.error("Cannot parse Visionary Code: invalid image dimensions",r,s),null;const v=Number(d)??0,b=Number(h)??0;return v<1||b<1?(console.error("Cannot parse Visionary Code: invalid blurhash x, y component dimensions",v,b),null):{altText:f,bcc:a,blurhash:c,blurhashX:v,blurhashY:b,sourceHeight:l,sourceWidth:p,url:m}};class _e extends Error{constructor(){super(...arguments),Ie(this,"message","invalid endpoint URL (does it contain http/https?)")}}const je=e=>e===C.DEBUG,Ve=e=>e===C.DOWNLOAD,$e=e=>Object.values($).includes(e),oe=e=>Object.values(y).includes(e),Le=(e=[])=>{const t={};for(const n of e)oe(n)?t.size=y[n]:je(n)?t.debug=!0:Ve(n)?t.download=!0:$e(n)&&(t.format=n);return t},ke=e=>{if(!e||typeof e!="object")return null;const t=[];return e.debug&&t.push(C.DEBUG),e.download&&t.push(C.DOWNLOAD),e.format&&e.format!==$.AUTO&&t.push(e.format),e.size&&oe(e.size)&&t.push(e.size),t.length?t.sort().join(","):null},ze=e=>{if(k(e)){const t=re(e);if(t)return{fields:t,options:{}}}return Be(e)},Be=e=>{if(!e)return null;const t=e.trim();if(!t)return null;try{const n=Ge(t);if(!n)return null;const{code:o,optionTokens:i}=n,r=re(o);if(!r)return null;const s=Le(i);return{fields:r,options:s}}catch(n){n instanceof Error?console.error(`Error parsing URL: ${n.message}`,n):console.error("uncaught error",n)}return null},Me=(e,t)=>{const n=De(e);if(n instanceof Error)return null;let o=null;if(t!=null&&t.endpoint&&(o=ne(t==null?void 0:t.endpoint),!o))throw new _e("Cannot construct URL: bad endpoint. Ensure endpoint starts with http:// or https://");o||(o=ne(ye));const i=[o.origin,"image",n],r=t?ke(t):null;return r&&i.push(r),t!=null&&t.filename?i.push(t.filename):i.push("image.jpg"),i.join("/")},Ge=e=>{try{const t=new URL(e),n=Ce(t.pathname.split("/"));if(n[0]!=="image"||![3,4].includes(n.length))throw new Error("Unrecognized URL");const o=n[1].trim();if(!o.length||!k(o))throw new Error("URL is not formatted as base64url");if(n.length===4){const i=n[2].split(",");return{code:o,optionTokens:i}}if(n.length===3)return{code:o,optionTokens:[]}}catch{return null}return null},se=.7,ie=1,U=24,We=y.lg,ce=process.env.NODE_ENV,Fe=ce==="development",ae=typeof document>"u",He=ce==="test",qe=ae?()=>{}:x.useLayoutEffect,Xe="<visionary>",T=(e,...t)=>{!console||typeof console.log!="function"||console.log(`${Xe} ${e}`,...t)},Ye="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~",z=(e,t,n)=>{let o=0;for(;t<n;)o*=83,o+=Ye.indexOf(e[t++]);return o},le=Math.pow,D=Math.PI,Ze=D*2,ue=3294.6,de=269.025,Je=e=>e>10.31475?le(e/de+.052132,2.4):e/ue,F=e=>~~(e>1227e-8?de*le(e,.416666)-13.025:e*ue+1),P=e=>(e<0?-1:1)*e*e,fe=e=>{for(e+=D/2;e>D;)e-=Ze;const t=1.27323954*e-.405284735*P(e);return .225*(P(t)-t)+t};function Ke(e){const t=z(e,2,6);return[t>>16,t>>8&255,t&255]}function Qe(e,t,n,o){const i=z(e,0,1),r=i%9+1,s=~~(i/9)+1,a=r*s;let c=0,d=0,h=0,f=0,m=0,p=0,l=0,v=0,b=0,g=0,w=0,A=0,u=0,S=0;const _=(z(e,1,2)+1)/13446*(o|1),I=new Float64Array(a*3),X=Ke(e);for(c=0;c<3;c++)I[c]=Je(X[c]);for(c=1;c<a;c++)S=z(e,4+c*2,6+c*2),I[c*3]=P(~~(S/(19*19))-9)*_,I[c*3+1]=P(~~(S/19)%19-9)*_,I[c*3+2]=P(S%19-9)*_;const j=t*4,O=new Uint8ClampedArray(j*n);for(f=0;f<n;f++)for(A=D*f/n,h=0;h<t;h++){for(m=0,p=0,l=0,u=D*h/t,d=0;d<s;d++)for(b=fe(A*d),c=0;c<r;c++)v=fe(u*c)*b,g=(c+d*r)*3,m+=I[g]*v,p+=I[g+1]*v,l+=I[g+2]*v;w=4*h+f*j,O[w]=F(m),O[w+1]=F(p),O[w+2]=F(l),O[w+3]=255}return O}const B=e=>{try{return new URL(e)}catch{return null}},et=(e,t)=>`rgba(${e.r},${e.g},${e.b},${t})`,H=e=>Fe||He?{"data-testid":e}:{},tt=(e,t)=>t?{"data-fileid":e}:{},pe=(e,t)=>Math.min(e,t),nt=e=>{let t=e.replace(/^#/,"");if(t.length!==3&&t.length!==6)return null;t.length===3&&(t=`${t[0]}${t[0]}${t[1]}${t[1]}${t[2]}${t[2]}`);const n=parseInt(t.slice(0,2),16),o=parseInt(t.slice(2,4),16);return{b:parseInt(t.slice(4,6),16),g:o,r:n}},N=(e,t=0)=>{const n=Math.pow(10,t);return Math.round(e*n)/n},rt=(e,t)=>{const n=B(e),o=B(t);return!n||!o?e:(o.pathname=n.pathname,o.search=n.search,o.toString())},ot=(e,t={},n=se,o=ie)=>{if(!e)return null;try{t.debug&&T("input imageSrc:",e);const i=ze(e);if(t.debug&&T("Visionary URL data: ",i),!i)throw new Error("Could not parse Visionary URL");const{fields:r,options:s}=i;if(r.sourceWidth<1||r.sourceHeight<1)throw new Error("Invalid image dimensions");const a=r.sourceWidth/r.sourceHeight,c=(t==null?void 0:t.size)??s.size??We,d=G[c];let h=0,f=0,m=0;if(a>=1){f=h=pe(d,r.sourceWidth);const g=N(f/a);m=N(f/g,6)}else h=pe(d,r.sourceHeight),f=N(h*a),m=N(f/h,6);const p=N(100/m,6),l={...r,arPaddingTop:`${p}%`,aspectRatio:m,maxWidth:f,src:e},v=B(r.url);if(v)l.src=v.toString();else if(!B(e)&&k(r.url)){const g=Me(r,{endpoint:t.endpoint,size:c});g&&(l.src=g)}t.endpoint&&(l.src=rt(l.src,t.endpoint));const b=r.bcc?nt(r.bcc):null;if(b&&(l.backgroundColor=et(b,n)),!ae&&r.blurhash&&!t.disableBlurLayer){const g=performance.now(),w=Qe(r.blurhash,U,U,o),A=performance.now()-g;t.debug&&T(`Blurhash decode time: ${N(A,1)} ms`),w&&(l.pixels=w)}return t.debug&&T("Visionary Image state: ",l),l}catch{return null}},he={bottom:0,left:0,position:"absolute",right:0,top:0},st={...he,maxHeight:"100%",maxWidth:"100%"},it={...he,height:"100%",width:"100%"},q={CANVAS:"canvas",CONTAINER:"container",IMAGE:"image"},M={container:"v7y_7587",hidden:"v7y_f2b7",preventDrag:"v7y_b8c0",preventSelection:"v7y_e0dd"},ct=({alt:e,bgColorAlpha:t=se,className:n,debug:o=!1,disableBlurLayer:i=!1,disableImageLayer:r=!1,endpoint:s,height:a,hideImageLayer:c,lazy:d=!0,onClick:h,onError:f,onLoad:m,preventDrag:p=!1,preventSelection:l=!1,punch:v=ie,size:b,src:g,style:w,width:A})=>{const u=x.useMemo(()=>ot(g,{debug:o,disableBlurLayer:i,endpoint:s,size:b},t,v),[t,o,i,s,v,b,g]),S=x.useRef(null),[_,I]=ve();qe(function(){if(!S.current||!(u!=null&&u.blurhash))return;if(!u.pixels){console.warn("No pixel data passed in, skipping");return}const ut=performance.now(),Z=S.current.getContext("2d");if(!Z){console.warn("Cannot access canvasRenderingContext, skipping");return}const J=Z.createImageData(U,U);if(!J){console.warn("Could not create ImageData on CanvasRenderingContext");return}if(J.data.set(u.pixels),Z.putImageData(J,0,0),o){const dt=performance.now()-ut;T(`Canvas render time: ${N(dt,1)} ms`)}},[u==null?void 0:u.blurhash,u==null?void 0:u.pixels,o]);const X=x.useCallback(V=>{o&&T("Image load error ",V),f&&f()},[o,f]),j={alt:e,loading:!d||I?"eager":"lazy",onLoad:m};if(!u){o&&T("Not a Visionary URL, rendering fallback <img />");const V={...j,className:n,height:a,onClick:h,onError:f,src:g,style:w,width:A};return R.jsx("img",{...V})}const O=Q(M.container,n,{[M.preventDrag]:!!p,[M.preventSelection]:!!l}),Y={...{"--v-ar":u.arPaddingTop},aspectRatio:u.aspectRatio,backgroundColor:(u==null?void 0:u.backgroundColor)??void 0,maxWidth:u.maxWidth,position:"relative",width:"100%",...w};A&&(Y.width=A),a&&(Y.height=a);const at={onClick:h},lt=Q({[M.hidden]:!!c});return R.jsxs("div",{className:O,ref:_,style:Y,...tt(u.url,o),...H(q.CONTAINER),...at,children:[!i&&R.jsx("canvas",{height:U,ref:S,style:it,width:U,...H(q.CANVAS)}),!r&&R.jsx("img",{...j,...H(q.IMAGE),className:lt,onError:X,src:u.src,style:st})]})};E.Image=ct,Object.defineProperty(E,Symbol.toStringTag,{value:"Module"})});
6
+ */(function(t){(function(){var e={}.hasOwnProperty;function n(){for(var r="",s=0;s<arguments.length;s++){var a=arguments[s];a&&(r=i(r,o(a)))}return r}function o(r){if(typeof r=="string"||typeof r=="number")return r;if(typeof r!="object")return"";if(Array.isArray(r))return n.apply(null,r);if(r.toString!==Object.prototype.toString&&!r.toString.toString().includes("[native code]"))return r.toString();var s="";for(var a in r)e.call(r,a)&&r[a]&&(s=i(s,a));return s}function i(r,s){return s?r?r+" "+s:r+s:r}t.exports?(n.default=n,t.exports=n):window.classNames=n})()})(K);var gt=K.exports;const mt=ht(gt),bt=(t,{root:e,rootMargin:n,threshold:o}={},i=[])=>{const r=I.useRef(null),s=I.useRef(null);return I.useCallback(c=>{r.current&&s.current&&(s.current.unobserve(r.current),s.current.disconnect(),s.current=null),c&&(s.current=new IntersectionObserver(t,{root:e,rootMargin:n,threshold:o}),s.current.observe(c),r.current=c)},[r,e,n,JSON.stringify(o),...i])},yt=({root:t,rootMargin:e,threshold:n,unobserveOnEnter:o,target:i,onEnter:r,onLeave:s,defaultInView:a}={},c=[])=>{const[d,h]=I.useState({inView:a||!1,entry:null,observer:null}),f=I.useCallback(([p],l)=>{const b=l.thresholds.some(m=>p.intersectionRatio>=m)&&p.isIntersecting;h({inView:b,entry:p,observer:l}),b&&o&&(l.unobserve(p.target),l.disconnect()),b?r==null||r(p,l):s==null||s(p,l)},[r,s,o]),g=bt(f,{root:t,rootMargin:e,threshold:n},[o,...c]);return I.useEffect(()=>{i!=null&&i.current&&g(i.current)},[i,g]),[g,d.inView,d.entry,d.observer]};var $=(t=>(t.AUTO="auto",t.AVIF="avif",t.JPEG="jpeg",t.WEBP="webp",t))($||{}),v=(t=>(t.xs="xs",t.sm="sm",t.md="md",t.lg="lg",t.xl="xl",t.xxl="xxl",t["4k"]="4k",t["5k"]="5k",t.full="full",t))(v||{}),C=(t=>(t.DEBUG="debug",t.DOWNLOAD="download",t))(C||{});const vt="https://link.visionary.cloud";$.AUTO,v.lg;const M={[v.xs]:160,[v.sm]:320,[v.md]:640,[v.lg]:1280,[v.xl]:1920,[v.xxl]:2560,[v["4k"]]:3840,[v["5k"]]:5120,[v.full]:0};Object.keys(M).reduce((t,e)=>{const n=e,o=M[n];return o&&(t[o]=n),t},{});const Q="!";var wt=Object.defineProperty,It=(t,e,n)=>e in t?wt(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,xt=(t,e,n)=>It(t,typeof e!="symbol"?e+"":e,n),G={},L={};Object.defineProperty(L,"__esModule",{value:!0});function Et(t){return String.fromCharCode(parseInt(t.slice(1),16))}function Rt(t){return btoa(encodeURIComponent(t).replace(/%[0-9A-F]{2}/g,Et))}L.encode=Rt;function At(t){return`%${`00${t.charCodeAt(0).toString(16)}`.slice(-2)}`}function St(t){return decodeURIComponent(Array.from(atob(t),At).join(""))}L.decode=St,Object.defineProperty(G,"__esModule",{value:!0});const tt=L;function Tt(t){return tt.decode(t.replace(/\-/g,"+").replace(/_/g,"/"))}var Ot=G.decode=Tt;function Nt(t){return tt.encode(t).replace(/\//g,"_").replace(/\+/g,"-").replace(/=+$/,"")}var Ut=G.encode=Nt;const Ct=t=>t.filter(Boolean),et=t=>{try{return new URL(t)}catch{return null}},k=(t="")=>/^[A-Za-z0-9_-]*$/.test(t),Dt=t=>{const{altText:e,bcc:n,blurhash:o,blurhashX:i,blurhashY:r,sourceHeight:s,sourceWidth:a,url:c}=t;if(!c||!a||!s)return new Error("Cannot construct visionary code: missing required url/width/height");const d=[c,a,s];return!n||(d.push(n),!o||!i||!r)||(d.push(o,i,r),e&&e.length&&d.push(e)),Pt(d)},Pt=t=>Ut(t.join(Q)),nt=t=>{if(typeof t!="string")return null;const e=t.trim();if(!e.length||!k(e))return null;const n=Ot(e);if(!n)return null;const o=n.split(Q);if(o.length<3)return null;const[i,r,s,a,c,d,h,f]=o,g=i.trim();if(!g.length)return console.error("Cannot parse code, empty file id"),null;const p=Number(r.trim()),l=Number(s.trim());if(isNaN(p)||isNaN(l)||!p||!l)return console.error("Cannot parse Visionary Code: invalid image dimensions",r,s),null;const y=Number(d)??0,b=Number(h)??0;return y<1||b<1?(console.error("Cannot parse Visionary Code: invalid blurhash x, y component dimensions",y,b),null):{altText:f,bcc:a,blurhash:c,blurhashX:y,blurhashY:b,sourceHeight:l,sourceWidth:p,url:g}};class _t extends Error{constructor(){super(...arguments),xt(this,"message","invalid endpoint URL (does it contain http/https?)")}}const jt=t=>t===C.DEBUG,Vt=t=>t===C.DOWNLOAD,$t=t=>Object.values($).includes(t),rt=t=>Object.values(v).includes(t),Lt=(t=[])=>{const e={};for(const n of t)rt(n)?e.size=v[n]:jt(n)?e.debug=!0:Vt(n)?e.download=!0:$t(n)&&(e.format=n);return e},kt=t=>{if(!t||typeof t!="object")return null;const e=[];return t.debug&&e.push(C.DEBUG),t.download&&e.push(C.DOWNLOAD),t.format&&t.format!==$.AUTO&&e.push(t.format),t.size&&rt(t.size)&&e.push(t.size),e.length?e.sort().join(","):null},zt=t=>{if(k(t)){const e=nt(t);if(e)return{fields:e,options:{}}}return Bt(t)},Bt=t=>{if(!t)return null;const e=t.trim();if(!e)return null;try{const n=Gt(e);if(!n)return null;const{code:o,optionTokens:i}=n,r=nt(o);if(!r)return null;const s=Lt(i);return{fields:r,options:s}}catch(n){n instanceof Error?console.error(`Error parsing URL: ${n.message}`,n):console.error("uncaught error",n)}return null},Mt=(t,e)=>{const n=Dt(t);if(n instanceof Error)return null;let o=null;if(e!=null&&e.endpoint&&(o=et(e==null?void 0:e.endpoint),!o))throw new _t("Cannot construct URL: bad endpoint. Ensure endpoint starts with http:// or https://");o||(o=et(vt));const i=[o.origin,"image",n],r=e?kt(e):null;return r&&i.push(r),e!=null&&e.filename?i.push(e.filename):i.push("image.jpg"),i.join("/")},Gt=t=>{try{const e=new URL(t),n=Ct(e.pathname.split("/"));if(n[0]!=="image"||![3,4].includes(n.length))throw new Error("Unrecognized URL");const o=n[1].trim();if(!o.length||!k(o))throw new Error("URL is not formatted as base64url");if(n.length===4){const i=n[2].split(",");return{code:o,optionTokens:i}}if(n.length===3)return{code:o,optionTokens:[]}}catch{return null}return null},ot=.7,st=1,U=24,Wt=v.lg,it=process.env.NODE_ENV,Ft=it==="development",ct=typeof document>"u",Ht=it==="test",qt=ct?()=>{}:I.useLayoutEffect,Xt="<visionary>",T=(t,...e)=>{!console||typeof console.log!="function"||console.log(`${Xt} ${t}`,...e)},Yt="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~",z=(t,e,n)=>{let o=0;for(;e<n;)o*=83,o+=Yt.indexOf(t[e++]);return o},at=Math.pow,D=Math.PI,Zt=D*2,lt=3294.6,ut=269.025,Jt=t=>t>10.31475?at(t/ut+.052132,2.4):t/lt,W=t=>~~(t>1227e-8?ut*at(t,.416666)-13.025:t*lt+1),P=t=>(t<0?-1:1)*t*t,dt=t=>{for(t+=D/2;t>D;)t-=Zt;const e=1.27323954*t-.405284735*P(t);return .225*(P(e)-e)+e};function Kt(t){const e=z(t,2,6);return[e>>16,e>>8&255,e&255]}function Qt(t,e,n,o){const i=z(t,0,1),r=i%9+1,s=~~(i/9)+1,a=r*s;let c=0,d=0,h=0,f=0,g=0,p=0,l=0,y=0,b=0,m=0,w=0,A=0,u=0,S=0;const _=(z(t,1,2)+1)/13446*(o|1),x=new Float64Array(a*3),X=Kt(t);for(c=0;c<3;c++)x[c]=Jt(X[c]);for(c=1;c<a;c++)S=z(t,4+c*2,6+c*2),x[c*3]=P(~~(S/(19*19))-9)*_,x[c*3+1]=P(~~(S/19)%19-9)*_,x[c*3+2]=P(S%19-9)*_;const j=e*4,N=new Uint8ClampedArray(j*n);for(f=0;f<n;f++)for(A=D*f/n,h=0;h<e;h++){for(g=0,p=0,l=0,u=D*h/e,d=0;d<s;d++)for(b=dt(A*d),c=0;c<r;c++)y=dt(u*c)*b,m=(c+d*r)*3,g+=x[m]*y,p+=x[m+1]*y,l+=x[m+2]*y;w=4*h+f*j,N[w]=W(g),N[w+1]=W(p),N[w+2]=W(l),N[w+3]=255}return N}const B=t=>{try{return new URL(t)}catch{return null}},te=(t,e)=>`rgba(${t.r},${t.g},${t.b},${e})`,F=t=>Ft||Ht?{"data-testid":t}:{},ee=(t,e)=>e?{"data-fileid":t}:{},ft=(t,e)=>Math.min(t,e),ne=t=>{let e=t.replace(/^#/,"");if(e.length!==3&&e.length!==6)return null;e.length===3&&(e=`${e[0]}${e[0]}${e[1]}${e[1]}${e[2]}${e[2]}`);const n=parseInt(e.slice(0,2),16),o=parseInt(e.slice(2,4),16);return{b:parseInt(e.slice(4,6),16),g:o,r:n}},O=(t,e=0)=>{const n=Math.pow(10,e);return Math.round(t*n)/n},re=(t,e)=>{const n=B(t),o=B(e);return!n||!o?t:(o.pathname=n.pathname,o.search=n.search,o.toString())},oe=(t,e={},n=ot,o=st)=>{if(!t)return null;try{e.debug&&T("input imageSrc:",t);const i=zt(t);if(e.debug&&T("Visionary URL data: ",i),!i)throw new Error("Could not parse Visionary URL");const{fields:r,options:s}=i;if(r.sourceWidth<1||r.sourceHeight<1)throw new Error("Invalid image dimensions");const a=r.sourceWidth/r.sourceHeight,c=(e==null?void 0:e.size)??s.size??Wt,d=M[c];let h=0,f=0,g=0;if(a>=1){f=h=ft(d,r.sourceWidth);const m=O(f/a);g=O(f/m,6)}else h=ft(d,r.sourceHeight),f=O(h*a),g=O(f/h,6);const p=O(100/g,6),l={...r,arPaddingTop:`${p}%`,aspectRatio:g,maxWidth:f,src:t},y=B(r.url);if(y)l.src=y.toString();else if(!B(t)&&k(r.url)){const m=Mt(r,{endpoint:e.endpoint,size:c});m&&(l.src=m)}e.endpoint&&(l.src=re(l.src,e.endpoint));const b=r.bcc?ne(r.bcc):null;if(b&&(l.backgroundColor=te(b,n)),!ct&&r.blurhash&&!e.disableBlurLayer){const m=performance.now(),w=Qt(r.blurhash,U,U,o),A=performance.now()-m;e.debug&&T(`Blurhash decode time: ${O(A,1)} ms`),w&&(l.pixels=w)}return e.debug&&T("Visionary Image state: ",l),l}catch{return null}},pt={bottom:0,left:0,position:"absolute",right:0,top:0},se={...pt,maxHeight:"100%",maxWidth:"100%"},ie={...pt,height:"100%",width:"100%"},ce=t=>{const e={...se};return t&&(e.display="none"),e},H={CANVAS:"canvas",CONTAINER:"container",IMAGE:"image"},q={container:"v7y_7587",preventDrag:"v7y_b8c0",preventSelection:"v7y_e0dd"},ae=({alt:t,bgColorAlpha:e=ot,className:n,debug:o=!1,disableBlurLayer:i=!1,disableImageLayer:r=!1,endpoint:s,height:a,hideImageLayer:c=!1,lazy:d=!0,onClick:h,onError:f,onLoad:g,preventDrag:p=!1,preventSelection:l=!1,punch:y=st,size:b,src:m,style:w,width:A})=>{const u=I.useMemo(()=>oe(m,{debug:o,disableBlurLayer:i,endpoint:s,size:b},e,y),[e,o,i,s,y,b,m]),S=I.useRef(null),[_,x]=yt();qt(function(){if(!S.current||!(u!=null&&u.blurhash))return;if(!u.pixels){console.warn("No pixel data passed in, skipping");return}const de=performance.now(),Z=S.current.getContext("2d");if(!Z){console.warn("Cannot access canvasRenderingContext, skipping");return}const J=Z.createImageData(U,U);if(!J){console.warn("Could not create ImageData on CanvasRenderingContext");return}if(J.data.set(u.pixels),Z.putImageData(J,0,0),o){const fe=performance.now()-de;T(`Canvas render time: ${O(fe,1)} ms`)}},[u==null?void 0:u.blurhash,u==null?void 0:u.pixels,o]);const X=I.useCallback(V=>{o&&T("Image load error ",V),f&&f()},[o,f]),j={alt:t,loading:!d||x?"eager":"lazy",onLoad:g};if(!u){o&&T("Not a Visionary URL, rendering fallback <img />");const V={...j,className:n,height:a,onClick:h,onError:f,src:m,style:w,width:A};return R.jsx("img",{...V})}const N=mt(q.container,n,{[q.preventDrag]:!!p,[q.preventSelection]:!!l}),Y={...{"--v-ar":u.arPaddingTop},aspectRatio:u.aspectRatio,backgroundColor:(u==null?void 0:u.backgroundColor)??void 0,maxWidth:u.maxWidth,position:"relative",width:"100%",...w};A&&(Y.width=A),a&&(Y.height=a);const le={onClick:h},ue=ce(c);return R.jsxs("div",{className:N,ref:_,style:Y,...ee(u.url,o),...F(H.CONTAINER),...le,children:[!i&&R.jsx("canvas",{height:U,ref:S,style:ie,width:U,...F(H.CANVAS)}),!r&&R.jsx("img",{...j,...F(H.IMAGE),onError:X,src:u.src,style:ue})]})};E.Image=ae,Object.defineProperty(E,Symbol.toStringTag,{value:"Module"})});
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "visionary-image",
3
- "version": "1.0.0",
3
+ "description": "React image component with built-in Blurhash placeholders for better UX and Core Web Vitals.",
4
+ "version": "1.0.1",
4
5
  "type": "module",
5
6
  "main": "./dist/visionary-image.umd.js",
6
7
  "module": "./dist/visionary-image.es.js",
@@ -85,12 +86,13 @@
85
86
  "core-web-vitals",
86
87
  "fid",
87
88
  "image",
88
- "lcp",
89
+ "inp",
89
90
  "performance",
90
91
  "placeholder",
91
92
  "react",
92
93
  "seo",
93
94
  "speed",
95
+ "typescript",
94
96
  "visionary",
95
97
  "web-vitals"
96
98
  ]