viconic-react-icons 1.5.5 → 1.5.7

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/dist/index.js CHANGED
@@ -1992,8 +1992,7 @@ var import_react = __toESM(require("react"));
1992
1992
  if (c && c.d && Date.now() - c.t < TTL) {
1993
1993
  for (const n in c.d) {
1994
1994
  if (!Object.prototype.hasOwnProperty.call(c.d, n)) continue;
1995
- const svg = c.d[n];
1996
- if (!W.icons[n]) W.icons[n] = svg;
1995
+ if (!W.icons[n]) W.icons[n] = c.d[n];
1997
1996
  }
1998
1997
  }
1999
1998
  } catch (e) {
@@ -2003,85 +2002,16 @@ var import_react = __toESM(require("react"));
2003
2002
  }
2004
2003
  })();
2005
2004
  var _initializedKits = /* @__PURE__ */ new Set();
2006
- var _kitRegistry = {};
2007
- var _pendingMisses = {};
2008
- if (typeof document !== "undefined") {
2009
- document.addEventListener("viconic:ready", (e) => {
2010
- const prefix = e && e.detail && e.detail.prefix;
2011
- if (prefix) _flushPendingMisses(prefix);
2012
- });
2013
- }
2014
- var _kitMissQueues = {};
2015
- var _kitMissTimers = {};
2016
- var _kitQueued = {};
2017
- function _resolveRegistry(prefix) {
2018
- if (_kitRegistry[prefix]) return _kitRegistry[prefix];
2019
- const kits = window.CopyIconsKit || {};
2020
- for (const kId in kits) {
2021
- const kit = kits[kId];
2022
- if (kit.config && kit.config.prefix === prefix) {
2023
- const entry = { kitId: kId, api: kit.config.api || "https://api.viconic.dev" };
2024
- _kitRegistry[prefix] = entry;
2025
- return entry;
2026
- }
2027
- }
2028
- return null;
2029
- }
2030
- function _flushPendingMisses(prefix) {
2031
- const pending = _pendingMisses[prefix];
2032
- if (!pending || !pending.size) return;
2033
- const entry = _resolveRegistry(prefix);
2034
- if (!entry) return;
2035
- delete _pendingMisses[prefix];
2036
- pending.forEach((name) => _dispatchBatchFetch(prefix, name, entry));
2037
- }
2038
- function _dispatchBatchFetch(prefix, iconBaseName, entry) {
2039
- const { kitId, api } = entry;
2040
- if (!_kitQueued[kitId]) _kitQueued[kitId] = /* @__PURE__ */ new Set();
2041
- if (_kitQueued[kitId].has(iconBaseName)) return;
2042
- _kitQueued[kitId].add(iconBaseName);
2043
- if (!_kitMissQueues[kitId]) _kitMissQueues[kitId] = /* @__PURE__ */ new Set();
2044
- _kitMissQueues[kitId].add(iconBaseName);
2045
- if (_kitMissTimers[kitId]) return;
2046
- _kitMissTimers[kitId] = setTimeout(() => {
2047
- delete _kitMissTimers[kitId];
2048
- const names = [..._kitMissQueues[kitId] || []];
2049
- delete _kitMissQueues[kitId];
2050
- if (!names.length) return;
2051
- fetch(`${api}/api/v1/kits/${kitId}/icons/?icons=${names.join(",")}`).then((r) => r.ok ? r.json() : null).then((bj) => {
2052
- if (!bj || !bj.icons) return;
2053
- const W = window.__viconic = window.__viconic || {};
2054
- W.icons = W.icons || {};
2055
- for (const n in bj.icons) {
2056
- if (!Object.prototype.hasOwnProperty.call(bj.icons, n)) continue;
2057
- const d = bj.icons[n];
2058
- const svg = d.svg || "";
2059
- if (!svg || !svg.includes("<")) continue;
2060
- W.icons[`@${prefix}/${n}`] = svg;
2061
- W.icons[`${prefix}:${n}`] = svg;
2062
- W.icons[`${prefix}/${n}`] = svg;
2063
- W.icons[n] = W.icons[n] || svg;
2064
- }
2065
- try {
2066
- document.dispatchEvent(new CustomEvent("viconic:ready", { detail: { prefix, kitId } }));
2067
- } catch (e) {
2068
- }
2069
- }).catch(() => {
2070
- });
2071
- }, 150);
2072
- }
2073
- function _queueKitMiss(prefix, iconBaseName) {
2074
- if (typeof window === "undefined") return;
2075
- const entry = _resolveRegistry(prefix);
2076
- if (entry) {
2077
- _dispatchBatchFetch(prefix, iconBaseName, entry);
2078
- } else {
2079
- if (!_pendingMisses[prefix]) _pendingMisses[prefix] = /* @__PURE__ */ new Set();
2080
- _pendingMisses[prefix].add(iconBaseName);
2081
- }
2005
+ function _isQueued(kitId, iconName) {
2006
+ if (typeof window === "undefined") return false;
2007
+ const q = window.__viconicQueued = window.__viconicQueued || {};
2008
+ const k = kitId + ":" + iconName;
2009
+ if (q[k]) return true;
2010
+ q[k] = 1;
2011
+ return false;
2082
2012
  }
2083
2013
  function initViconic(options = {}) {
2084
- const { kitId, cdnBase = "cdn.viconic.dev", version, prefix: explicitPrefix, api = "https://api.viconic.dev" } = options;
2014
+ const { kitId, cdnBase = "cdn.viconic.dev", version } = options;
2085
2015
  if (typeof window === "undefined") return;
2086
2016
  if (!kitId) {
2087
2017
  console.warn("[Viconic] initViconic requires a kitId");
@@ -2089,31 +2019,14 @@ function initViconic(options = {}) {
2089
2019
  }
2090
2020
  if (_initializedKits.has(kitId)) return;
2091
2021
  _initializedKits.add(kitId);
2092
- if (explicitPrefix) {
2093
- _kitRegistry[explicitPrefix] = { kitId, api };
2094
- }
2095
- if (typeof window !== "undefined") {
2096
- window.__viconicKitIds = window.__viconicKitIds || {};
2097
- window.__viconicKitIds[kitId] = { api };
2098
- }
2099
2022
  const scriptId = `viconic-kit-${kitId}`;
2100
2023
  if (document.getElementById(scriptId)) return;
2101
2024
  const script = document.createElement("script");
2102
2025
  script.id = scriptId;
2103
2026
  let url = `https://${cdnBase}/kits/${kitId}/loader.js`;
2104
2027
  if (version) {
2105
- const vQuery = version === "dev" ? Date.now() : version;
2106
- url += `?v=${vQuery}`;
2028
+ url += `?v=${version === "dev" ? Date.now() : version}`;
2107
2029
  }
2108
- script.onload = () => {
2109
- const kits = window.CopyIconsKit || {};
2110
- const kit = kits[kitId];
2111
- if (kit && kit.config && kit.config.prefix) {
2112
- const pfx = kit.config.prefix;
2113
- _kitRegistry[pfx] = { kitId, api: kit.config.api || api };
2114
- _flushPendingMisses(pfx);
2115
- }
2116
- };
2117
2030
  script.src = url;
2118
2031
  script.async = true;
2119
2032
  document.head.appendChild(script);
@@ -2134,51 +2047,50 @@ var ViconicIcon = ({ name, className, style, size, color, ...props }) => {
2134
2047
  function isInjected() {
2135
2048
  return el.classList.contains("vi-ok") || el.classList.contains("svg-loaded");
2136
2049
  }
2137
- if (!isInjected()) {
2050
+ function tryInjectFromCache() {
2051
+ if (isInjected()) return false;
2138
2052
  const W = window.__viconic;
2139
- if (W && W.icons) {
2140
- const iconBaseName = prefixMatch ? prefixMatch[2] : null;
2141
- const svg = iconBaseName && W.icons[iconBaseName] || name && W.icons[name] || null;
2142
- if (svg) {
2143
- el.innerHTML = svg;
2144
- el.classList.add("vi-ok", "svg-loaded");
2145
- }
2053
+ if (!W || !W.icons) return false;
2054
+ const baseName = prefixMatch ? prefixMatch[2] : null;
2055
+ const svg = baseName && W.icons[baseName] || name && W.icons[name] || null;
2056
+ if (svg) {
2057
+ el.innerHTML = svg;
2058
+ el.classList.add("vi-ok", "svg-loaded");
2059
+ return true;
2146
2060
  }
2061
+ return false;
2147
2062
  }
2148
- if (!isInjected() && !isKitIcon) {
2063
+ if (tryInjectFromCache()) return;
2064
+ if (!isKitIcon) {
2149
2065
  if (window.CopyIcons && window.CopyIcons.forceProcess) {
2150
2066
  window.CopyIcons.forceProcess(el);
2067
+ } else if (el._u) {
2068
+ el._u();
2151
2069
  }
2152
2070
  }
2153
- if (!isInjected() && !isKitIcon && el._u) {
2154
- el._u();
2155
- }
2156
2071
  if (isInjected()) return;
2157
- function tryInjectFromEvent() {
2158
- if (isInjected()) return;
2159
- const W = window.__viconic;
2160
- if (W && W.icons) {
2161
- const iconBaseName = prefixMatch ? prefixMatch[2] : null;
2162
- const svg = iconBaseName && W.icons[iconBaseName] || name && W.icons[name] || null;
2163
- if (svg) {
2164
- el.innerHTML = svg;
2165
- el.classList.add("vi-ok", "svg-loaded");
2166
- return;
2072
+ if (isKitIcon && prefixMatch) {
2073
+ const kitId = (() => {
2074
+ const kits = window.CopyIconsKit || {};
2075
+ for (const kId in kits) {
2076
+ if (kits[kId].config && kits[kId].config.prefix === prefixMatch[1]) return kId;
2077
+ }
2078
+ return prefixMatch[1];
2079
+ })();
2080
+ if (!_isQueued(kitId, prefixMatch[2])) {
2081
+ if (el._u) {
2082
+ el._u();
2167
2083
  }
2168
- }
2169
- if (!isKitIcon && el._u) el._u();
2170
- if (!isKitIcon && window.CopyIcons && window.CopyIcons.forceProcess) {
2171
- window.CopyIcons.forceProcess(el);
2172
2084
  }
2173
2085
  }
2174
- document.addEventListener("viconic:ready", tryInjectFromEvent);
2175
- if (!isInjected() && isKitIcon && prefixMatch) {
2176
- const prefix = prefixMatch[1];
2177
- const iconBaseName = prefixMatch[2];
2178
- _queueKitMiss(prefix, iconBaseName);
2086
+ function onReady() {
2087
+ if (tryInjectFromCache()) {
2088
+ document.removeEventListener("viconic:ready", onReady);
2089
+ }
2179
2090
  }
2091
+ document.addEventListener("viconic:ready", onReady);
2180
2092
  return () => {
2181
- document.removeEventListener("viconic:ready", tryInjectFromEvent);
2093
+ document.removeEventListener("viconic:ready", onReady);
2182
2094
  };
2183
2095
  }, [name, className, size, color, style]);
2184
2096
  const combinedStyle = {
package/dist/index.mjs CHANGED
@@ -1957,8 +1957,7 @@ import React, { useEffect, useRef } from "react";
1957
1957
  if (c && c.d && Date.now() - c.t < TTL) {
1958
1958
  for (const n in c.d) {
1959
1959
  if (!Object.prototype.hasOwnProperty.call(c.d, n)) continue;
1960
- const svg = c.d[n];
1961
- if (!W.icons[n]) W.icons[n] = svg;
1960
+ if (!W.icons[n]) W.icons[n] = c.d[n];
1962
1961
  }
1963
1962
  }
1964
1963
  } catch (e) {
@@ -1968,85 +1967,16 @@ import React, { useEffect, useRef } from "react";
1968
1967
  }
1969
1968
  })();
1970
1969
  var _initializedKits = /* @__PURE__ */ new Set();
1971
- var _kitRegistry = {};
1972
- var _pendingMisses = {};
1973
- if (typeof document !== "undefined") {
1974
- document.addEventListener("viconic:ready", (e) => {
1975
- const prefix = e && e.detail && e.detail.prefix;
1976
- if (prefix) _flushPendingMisses(prefix);
1977
- });
1978
- }
1979
- var _kitMissQueues = {};
1980
- var _kitMissTimers = {};
1981
- var _kitQueued = {};
1982
- function _resolveRegistry(prefix) {
1983
- if (_kitRegistry[prefix]) return _kitRegistry[prefix];
1984
- const kits = window.CopyIconsKit || {};
1985
- for (const kId in kits) {
1986
- const kit = kits[kId];
1987
- if (kit.config && kit.config.prefix === prefix) {
1988
- const entry = { kitId: kId, api: kit.config.api || "https://api.viconic.dev" };
1989
- _kitRegistry[prefix] = entry;
1990
- return entry;
1991
- }
1992
- }
1993
- return null;
1994
- }
1995
- function _flushPendingMisses(prefix) {
1996
- const pending = _pendingMisses[prefix];
1997
- if (!pending || !pending.size) return;
1998
- const entry = _resolveRegistry(prefix);
1999
- if (!entry) return;
2000
- delete _pendingMisses[prefix];
2001
- pending.forEach((name) => _dispatchBatchFetch(prefix, name, entry));
2002
- }
2003
- function _dispatchBatchFetch(prefix, iconBaseName, entry) {
2004
- const { kitId, api } = entry;
2005
- if (!_kitQueued[kitId]) _kitQueued[kitId] = /* @__PURE__ */ new Set();
2006
- if (_kitQueued[kitId].has(iconBaseName)) return;
2007
- _kitQueued[kitId].add(iconBaseName);
2008
- if (!_kitMissQueues[kitId]) _kitMissQueues[kitId] = /* @__PURE__ */ new Set();
2009
- _kitMissQueues[kitId].add(iconBaseName);
2010
- if (_kitMissTimers[kitId]) return;
2011
- _kitMissTimers[kitId] = setTimeout(() => {
2012
- delete _kitMissTimers[kitId];
2013
- const names = [..._kitMissQueues[kitId] || []];
2014
- delete _kitMissQueues[kitId];
2015
- if (!names.length) return;
2016
- fetch(`${api}/api/v1/kits/${kitId}/icons/?icons=${names.join(",")}`).then((r) => r.ok ? r.json() : null).then((bj) => {
2017
- if (!bj || !bj.icons) return;
2018
- const W = window.__viconic = window.__viconic || {};
2019
- W.icons = W.icons || {};
2020
- for (const n in bj.icons) {
2021
- if (!Object.prototype.hasOwnProperty.call(bj.icons, n)) continue;
2022
- const d = bj.icons[n];
2023
- const svg = d.svg || "";
2024
- if (!svg || !svg.includes("<")) continue;
2025
- W.icons[`@${prefix}/${n}`] = svg;
2026
- W.icons[`${prefix}:${n}`] = svg;
2027
- W.icons[`${prefix}/${n}`] = svg;
2028
- W.icons[n] = W.icons[n] || svg;
2029
- }
2030
- try {
2031
- document.dispatchEvent(new CustomEvent("viconic:ready", { detail: { prefix, kitId } }));
2032
- } catch (e) {
2033
- }
2034
- }).catch(() => {
2035
- });
2036
- }, 150);
2037
- }
2038
- function _queueKitMiss(prefix, iconBaseName) {
2039
- if (typeof window === "undefined") return;
2040
- const entry = _resolveRegistry(prefix);
2041
- if (entry) {
2042
- _dispatchBatchFetch(prefix, iconBaseName, entry);
2043
- } else {
2044
- if (!_pendingMisses[prefix]) _pendingMisses[prefix] = /* @__PURE__ */ new Set();
2045
- _pendingMisses[prefix].add(iconBaseName);
2046
- }
1970
+ function _isQueued(kitId, iconName) {
1971
+ if (typeof window === "undefined") return false;
1972
+ const q = window.__viconicQueued = window.__viconicQueued || {};
1973
+ const k = kitId + ":" + iconName;
1974
+ if (q[k]) return true;
1975
+ q[k] = 1;
1976
+ return false;
2047
1977
  }
2048
1978
  function initViconic(options = {}) {
2049
- const { kitId, cdnBase = "cdn.viconic.dev", version, prefix: explicitPrefix, api = "https://api.viconic.dev" } = options;
1979
+ const { kitId, cdnBase = "cdn.viconic.dev", version } = options;
2050
1980
  if (typeof window === "undefined") return;
2051
1981
  if (!kitId) {
2052
1982
  console.warn("[Viconic] initViconic requires a kitId");
@@ -2054,31 +1984,14 @@ function initViconic(options = {}) {
2054
1984
  }
2055
1985
  if (_initializedKits.has(kitId)) return;
2056
1986
  _initializedKits.add(kitId);
2057
- if (explicitPrefix) {
2058
- _kitRegistry[explicitPrefix] = { kitId, api };
2059
- }
2060
- if (typeof window !== "undefined") {
2061
- window.__viconicKitIds = window.__viconicKitIds || {};
2062
- window.__viconicKitIds[kitId] = { api };
2063
- }
2064
1987
  const scriptId = `viconic-kit-${kitId}`;
2065
1988
  if (document.getElementById(scriptId)) return;
2066
1989
  const script = document.createElement("script");
2067
1990
  script.id = scriptId;
2068
1991
  let url = `https://${cdnBase}/kits/${kitId}/loader.js`;
2069
1992
  if (version) {
2070
- const vQuery = version === "dev" ? Date.now() : version;
2071
- url += `?v=${vQuery}`;
1993
+ url += `?v=${version === "dev" ? Date.now() : version}`;
2072
1994
  }
2073
- script.onload = () => {
2074
- const kits = window.CopyIconsKit || {};
2075
- const kit = kits[kitId];
2076
- if (kit && kit.config && kit.config.prefix) {
2077
- const pfx = kit.config.prefix;
2078
- _kitRegistry[pfx] = { kitId, api: kit.config.api || api };
2079
- _flushPendingMisses(pfx);
2080
- }
2081
- };
2082
1995
  script.src = url;
2083
1996
  script.async = true;
2084
1997
  document.head.appendChild(script);
@@ -2099,51 +2012,50 @@ var ViconicIcon = ({ name, className, style, size, color, ...props }) => {
2099
2012
  function isInjected() {
2100
2013
  return el.classList.contains("vi-ok") || el.classList.contains("svg-loaded");
2101
2014
  }
2102
- if (!isInjected()) {
2015
+ function tryInjectFromCache() {
2016
+ if (isInjected()) return false;
2103
2017
  const W = window.__viconic;
2104
- if (W && W.icons) {
2105
- const iconBaseName = prefixMatch ? prefixMatch[2] : null;
2106
- const svg = iconBaseName && W.icons[iconBaseName] || name && W.icons[name] || null;
2107
- if (svg) {
2108
- el.innerHTML = svg;
2109
- el.classList.add("vi-ok", "svg-loaded");
2110
- }
2018
+ if (!W || !W.icons) return false;
2019
+ const baseName = prefixMatch ? prefixMatch[2] : null;
2020
+ const svg = baseName && W.icons[baseName] || name && W.icons[name] || null;
2021
+ if (svg) {
2022
+ el.innerHTML = svg;
2023
+ el.classList.add("vi-ok", "svg-loaded");
2024
+ return true;
2111
2025
  }
2026
+ return false;
2112
2027
  }
2113
- if (!isInjected() && !isKitIcon) {
2028
+ if (tryInjectFromCache()) return;
2029
+ if (!isKitIcon) {
2114
2030
  if (window.CopyIcons && window.CopyIcons.forceProcess) {
2115
2031
  window.CopyIcons.forceProcess(el);
2032
+ } else if (el._u) {
2033
+ el._u();
2116
2034
  }
2117
2035
  }
2118
- if (!isInjected() && !isKitIcon && el._u) {
2119
- el._u();
2120
- }
2121
2036
  if (isInjected()) return;
2122
- function tryInjectFromEvent() {
2123
- if (isInjected()) return;
2124
- const W = window.__viconic;
2125
- if (W && W.icons) {
2126
- const iconBaseName = prefixMatch ? prefixMatch[2] : null;
2127
- const svg = iconBaseName && W.icons[iconBaseName] || name && W.icons[name] || null;
2128
- if (svg) {
2129
- el.innerHTML = svg;
2130
- el.classList.add("vi-ok", "svg-loaded");
2131
- return;
2037
+ if (isKitIcon && prefixMatch) {
2038
+ const kitId = (() => {
2039
+ const kits = window.CopyIconsKit || {};
2040
+ for (const kId in kits) {
2041
+ if (kits[kId].config && kits[kId].config.prefix === prefixMatch[1]) return kId;
2042
+ }
2043
+ return prefixMatch[1];
2044
+ })();
2045
+ if (!_isQueued(kitId, prefixMatch[2])) {
2046
+ if (el._u) {
2047
+ el._u();
2132
2048
  }
2133
- }
2134
- if (!isKitIcon && el._u) el._u();
2135
- if (!isKitIcon && window.CopyIcons && window.CopyIcons.forceProcess) {
2136
- window.CopyIcons.forceProcess(el);
2137
2049
  }
2138
2050
  }
2139
- document.addEventListener("viconic:ready", tryInjectFromEvent);
2140
- if (!isInjected() && isKitIcon && prefixMatch) {
2141
- const prefix = prefixMatch[1];
2142
- const iconBaseName = prefixMatch[2];
2143
- _queueKitMiss(prefix, iconBaseName);
2051
+ function onReady() {
2052
+ if (tryInjectFromCache()) {
2053
+ document.removeEventListener("viconic:ready", onReady);
2054
+ }
2144
2055
  }
2056
+ document.addEventListener("viconic:ready", onReady);
2145
2057
  return () => {
2146
- document.removeEventListener("viconic:ready", tryInjectFromEvent);
2058
+ document.removeEventListener("viconic:ready", onReady);
2147
2059
  };
2148
2060
  }, [name, className, size, color, style]);
2149
2061
  const combinedStyle = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "viconic-react-icons",
3
- "version": "1.5.5",
3
+ "version": "1.5.7",
4
4
  "description": "Viconic Smart Icons loader for React — supports Kit and 200k+ system icons",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
package/src/index.jsx CHANGED
@@ -3,32 +3,27 @@ import './copyicons-smart-loader.js';
3
3
 
4
4
  // ============================================
5
5
  // PRE-LOAD KIT CACHE SYNCHRONOUSLY AT MODULE LEVEL
6
- // Same architecture as kit loader: read localStorage before any render
7
- // so icons are available instantly when components mount.
8
6
  // ============================================
9
7
  (function _preloadKitCache() {
10
8
  if (typeof window === 'undefined' || typeof localStorage === 'undefined') return;
11
9
  try {
12
10
  const W = (window.__viconic = window.__viconic || {});
13
11
  W.icons = W.icons || {};
14
- const TTL = 3 * 24 * 60 * 60 * 1000; // 3 days
12
+ const TTL = 3 * 24 * 60 * 60 * 1000;
15
13
  for (let i = 0; i < localStorage.length; i++) {
16
14
  const key = localStorage.key(i);
17
15
  if (!key || !key.startsWith('viconic:')) continue;
18
16
  try {
19
17
  const c = JSON.parse(localStorage.getItem(key));
20
18
  if (c && c.d && Date.now() - c.t < TTL) {
21
- // Register all icon name variants into W.icons
22
19
  for (const n in c.d) {
23
20
  if (!Object.prototype.hasOwnProperty.call(c.d, n)) continue;
24
- const svg = c.d[n];
25
- // Store by plain name so _u() can find it regardless of prefix
26
- if (!W.icons[n]) W.icons[n] = svg;
21
+ if (!W.icons[n]) W.icons[n] = c.d[n];
27
22
  }
28
23
  }
29
- } catch (e) { /* corrupted entry, skip */ }
24
+ } catch (e) {}
30
25
  }
31
- } catch (e) { }
26
+ } catch (e) {}
32
27
  })();
33
28
 
34
29
  // ============================================
@@ -37,169 +32,58 @@ import './copyicons-smart-loader.js';
37
32
  const _initializedKits = new Set();
38
33
 
39
34
  // ============================================
40
- // Kit registry: prefix -> { kitId, api }
41
- // Populated by initViconic() via script.onload
42
- // and by viconic:ready event handler.
35
+ // Window-level dedup set: survives HMR module reloads and Strict Mode remounts.
36
+ // Keyed by iconKey = "kitId:iconName". Cleared after loader fires viconic:ready.
43
37
  // ============================================
44
- const _kitRegistry = {}; // prefix -> { kitId, api }
45
-
46
- // Pending misses buffered before registry is ready
47
- // prefix -> Set of icon names
48
- const _pendingMisses = {};
49
-
50
- // Global viconic:ready listener — fires when kit loader calls register()
51
- // This is the main trigger for flushing pending misses when loader.js
52
- // completes its first batch fetch (which happens after script.onload).
53
- if (typeof document !== 'undefined') {
54
- document.addEventListener('viconic:ready', (e) => {
55
- const prefix = e && e.detail && e.detail.prefix;
56
- if (prefix) _flushPendingMisses(prefix);
57
- });
58
- }
59
-
60
- // ============================================
61
- // DEBOUNCED BATCH FETCH for missing kit icons
62
- // ============================================
63
- const _kitMissQueues = {}; // kitId -> Set of icon names
64
- const _kitMissTimers = {}; // kitId -> timer handle
65
- const _kitQueued = {}; // kitId -> Set of icon names already queued (prevents Strict Mode double-queue)
66
-
67
- function _resolveRegistry(prefix) {
68
- if (_kitRegistry[prefix]) return _kitRegistry[prefix];
69
- // Fallback: check CopyIconsKit
70
- const kits = window.CopyIconsKit || {};
71
- for (const kId in kits) {
72
- const kit = kits[kId];
73
- if (kit.config && kit.config.prefix === prefix) {
74
- const entry = { kitId: kId, api: kit.config.api || 'https://api.viconic.dev' };
75
- _kitRegistry[prefix] = entry;
76
- return entry;
77
- }
78
- }
79
- return null;
38
+ function _isQueued(kitId, iconName) {
39
+ if (typeof window === 'undefined') return false;
40
+ const q = (window.__viconicQueued = window.__viconicQueued || {});
41
+ const k = kitId + ':' + iconName;
42
+ if (q[k]) return true;
43
+ q[k] = 1;
44
+ return false;
80
45
  }
81
46
 
82
- function _flushPendingMisses(prefix) {
83
- const pending = _pendingMisses[prefix];
84
- if (!pending || !pending.size) return;
85
- const entry = _resolveRegistry(prefix);
86
- if (!entry) return;
87
- delete _pendingMisses[prefix];
88
- pending.forEach(name => _dispatchBatchFetch(prefix, name, entry));
89
- }
90
-
91
- function _dispatchBatchFetch(prefix, iconBaseName, entry) {
92
- const { kitId, api } = entry;
93
- // Skip if already queued — prevents React Strict Mode double-invoke from making 2 requests
94
- if (!_kitQueued[kitId]) _kitQueued[kitId] = new Set();
95
- if (_kitQueued[kitId].has(iconBaseName)) return;
96
- _kitQueued[kitId].add(iconBaseName);
97
- if (!_kitMissQueues[kitId]) _kitMissQueues[kitId] = new Set();
98
- _kitMissQueues[kitId].add(iconBaseName);
99
- if (_kitMissTimers[kitId]) return; // already scheduled, will pick up new additions
100
- // 150ms debounce: collect all icons across the full React render cycle
101
- _kitMissTimers[kitId] = setTimeout(() => {
102
- delete _kitMissTimers[kitId];
103
- const names = [...(_kitMissQueues[kitId] || [])];
104
- delete _kitMissQueues[kitId];
105
- if (!names.length) return;
106
- fetch(`${api}/api/v1/kits/${kitId}/icons/?icons=${names.join(',')}`)
107
- .then(r => r.ok ? r.json() : null)
108
- .then(bj => {
109
- if (!bj || !bj.icons) return;
110
- const W = (window.__viconic = window.__viconic || {});
111
- W.icons = W.icons || {};
112
- for (const n in bj.icons) {
113
- if (!Object.prototype.hasOwnProperty.call(bj.icons, n)) continue;
114
- const d = bj.icons[n];
115
- const svg = d.svg || '';
116
- if (!svg || !svg.includes('<')) continue;
117
- W.icons[`@${prefix}/${n}`] = svg;
118
- W.icons[`${prefix}:${n}`] = svg;
119
- W.icons[`${prefix}/${n}`] = svg;
120
- W.icons[n] = W.icons[n] || svg;
121
- }
122
- try { document.dispatchEvent(new CustomEvent('viconic:ready', { detail: { prefix, kitId } })); } catch(e) {}
123
- })
124
- .catch(() => {});
125
- }, 150);
126
- }
127
-
128
- function _queueKitMiss(prefix, iconBaseName) {
47
+ function _clearQueued(kitId) {
129
48
  if (typeof window === 'undefined') return;
130
- const entry = _resolveRegistry(prefix);
131
- if (entry) {
132
- _dispatchBatchFetch(prefix, iconBaseName, entry);
133
- } else {
134
- // Registry not ready yet — buffer until loader fires
135
- if (!_pendingMisses[prefix]) _pendingMisses[prefix] = new Set();
136
- _pendingMisses[prefix].add(iconBaseName);
49
+ const q = window.__viconicQueued;
50
+ if (!q) return;
51
+ const prefix = kitId + ':';
52
+ for (const k in q) {
53
+ if (k.startsWith(prefix)) delete q[k];
137
54
  }
138
55
  }
139
56
 
140
57
  /**
141
58
  * Initialize a Viconic Kit in your React app.
142
59
  * Injects the kit's loader.js script into <head>.
143
- * Supports multiple kits — call once per kit.
144
60
  *
145
61
  * @param {Object} options
146
62
  * @param {string} options.kitId - UUID of the Kit (required)
147
63
  * @param {string} [options.cdnBase] - Custom CDN domain (default: cdn.viconic.dev)
148
- *
149
- * @example
150
- * import { initViconic } from 'viconic-react-icons';
151
- *
152
- * initViconic({ kitId: '387a6161-cb39-411f-8f13-29a5813e4efd' });
153
- * // Multiple kits:
154
- * initViconic({ kitId: 'another-kit-uuid', version: '1.0' });
64
+ * @param {string} [options.version] - Version string for cache busting
155
65
  */
156
66
  export function initViconic(options = {}) {
157
- const { kitId, cdnBase = 'cdn.viconic.dev', version, prefix: explicitPrefix, api = 'https://api.viconic.dev' } = options;
158
- if (typeof window === 'undefined') return; // SSR guard
67
+ const { kitId, cdnBase = 'cdn.viconic.dev', version } = options;
68
+ if (typeof window === 'undefined') return;
159
69
  if (!kitId) {
160
70
  console.warn('[Viconic] initViconic requires a kitId');
161
71
  return;
162
72
  }
163
- if (_initializedKits.has(kitId)) return; // Already initialized
73
+ if (_initializedKits.has(kitId)) return;
164
74
  _initializedKits.add(kitId);
165
75
 
166
- // Register synchronously so _queueKitMiss can find kitId immediately,
167
- // even before the async loader.js script has loaded and populated CopyIconsKit.
168
- // If prefix is not known yet, we'll also register once CopyIconsKit fires.
169
- if (explicitPrefix) {
170
- _kitRegistry[explicitPrefix] = { kitId, api };
171
- }
172
- // Also register by kitId so we can backfill prefix once loader fires
173
- if (typeof window !== 'undefined') {
174
- window.__viconicKitIds = window.__viconicKitIds || {};
175
- window.__viconicKitIds[kitId] = { api };
176
- }
177
-
178
76
  const scriptId = `viconic-kit-${kitId}`;
179
- if (document.getElementById(scriptId)) return; // Script already in DOM
77
+ if (document.getElementById(scriptId)) return;
180
78
 
181
79
  const script = document.createElement('script');
182
80
  script.id = scriptId;
183
81
 
184
- // Add cache busting for development or specific version updates
185
82
  let url = `https://${cdnBase}/kits/${kitId}/loader.js`;
186
83
  if (version) {
187
- const vQuery = version === 'dev' ? Date.now() : version;
188
- url += `?v=${vQuery}`;
84
+ url += `?v=${version === 'dev' ? Date.now() : version}`;
189
85
  }
190
86
 
191
- // Once loader.js runs and registers CopyIconsKit, backfill _kitRegistry and flush pending misses
192
- script.onload = () => {
193
- const kits = window.CopyIconsKit || {};
194
- const kit = kits[kitId];
195
- if (kit && kit.config && kit.config.prefix) {
196
- const pfx = kit.config.prefix;
197
- _kitRegistry[pfx] = { kitId, api: kit.config.api || api };
198
- // Flush any misses buffered before the loader was ready
199
- _flushPendingMisses(pfx);
200
- }
201
- };
202
-
203
87
  script.src = url;
204
88
  script.async = true;
205
89
  document.head.appendChild(script);
@@ -207,11 +91,7 @@ export function initViconic(options = {}) {
207
91
 
208
92
  /**
209
93
  * ViconicIcon — React component for rendering Viconic icons.
210
- *
211
- * Works with both:
212
- * - System icons: <ViconicIcon name="lucide:home" />
213
- * - Kit icons: <ViconicIcon name="@myprefix/home" /> (after initViconic)
214
- *
94
+ *
215
95
  * @param {string} name - Icon identifier (e.g., "lucide:home", "@prefix/name")
216
96
  * @param {string} [className] - CSS class names
217
97
  * @param {object} [style] - Inline styles
@@ -226,7 +106,6 @@ export const ViconicIcon = ({ name, className, style, size, color, ...props }) =
226
106
  const el = iconRef.current;
227
107
  if (!el) return;
228
108
 
229
- // --- FIX DOM REUSE (Reconciliation) ---
230
109
  if (prevNameRef.current !== name) {
231
110
  el.innerHTML = '';
232
111
  el.classList.remove('vi-ok', 'svg-loaded', 'vi-mono', 'ci-multicolor');
@@ -240,78 +119,73 @@ export const ViconicIcon = ({ name, className, style, size, color, ...props }) =
240
119
  return el.classList.contains('vi-ok') || el.classList.contains('svg-loaded');
241
120
  }
242
121
 
243
- // --- PATH 1: Synchronous inject from W.icons (pre-loaded from localStorage at module init) ---
244
- if (!isInjected()) {
122
+ function tryInjectFromCache() {
123
+ if (isInjected()) return false;
245
124
  const W = window.__viconic;
246
- if (W && W.icons) {
247
- // Try all key variants: iconBaseName, @prefix/name, prefix:name, prefix/name
248
- const iconBaseName = prefixMatch ? prefixMatch[2] : null;
249
- const svg = (iconBaseName && W.icons[iconBaseName])
250
- || (name && W.icons[name])
251
- || null;
252
- if (svg) {
253
- el.innerHTML = svg;
254
- el.classList.add('vi-ok', 'svg-loaded');
255
- }
125
+ if (!W || !W.icons) return false;
126
+ const baseName = prefixMatch ? prefixMatch[2] : null;
127
+ const svg = (baseName && W.icons[baseName])
128
+ || (name && W.icons[name])
129
+ || null;
130
+ if (svg) {
131
+ el.innerHTML = svg;
132
+ el.classList.add('vi-ok', 'svg-loaded');
133
+ return true;
256
134
  }
135
+ return false;
257
136
  }
258
137
 
259
- // --- PATH 2: Smart loader for system icons (prefix:name) ---
260
- if (!isInjected() && !isKitIcon) {
138
+ // PATH 1: Already in W.icons (from localStorage preload or prior fetch)
139
+ if (tryInjectFromCache()) return;
140
+
141
+ // PATH 2: System icons — delegate to smart loader
142
+ if (!isKitIcon) {
261
143
  if (window.CopyIcons && window.CopyIcons.forceProcess) {
262
144
  window.CopyIcons.forceProcess(el);
145
+ } else if (el._u) {
146
+ el._u();
263
147
  }
264
148
  }
265
149
 
266
- // --- PATH 3: Custom element _u() call (kit loader registered element, system icons only) ---
267
- if (!isInjected() && !isKitIcon && el._u) {
268
- el._u();
269
- }
270
-
271
150
  if (isInjected()) return;
272
151
 
273
- // --- PATH 4: Event-drivenlisten for viconic:ready fired by kit loader after register() ---
274
- // Much faster than polling: icons appear the instant the loader finishes fetching them.
275
- function tryInjectFromEvent() {
276
- if (isInjected()) return;
277
- const W = window.__viconic;
278
- if (W && W.icons) {
279
- const iconBaseName = prefixMatch ? prefixMatch[2] : null;
280
- const svg = (iconBaseName && W.icons[iconBaseName])
281
- || (name && W.icons[name])
282
- || null;
283
- if (svg) {
284
- el.innerHTML = svg;
285
- el.classList.add('vi-ok', 'svg-loaded');
286
- return;
152
+ // PATH 3: Kit icons let loader.js handle batching via el._u()
153
+ // el._u() notifies the loader element to queue this icon.
154
+ // Loader already has its own debounced batch queue.
155
+ // Use window-level dedup to prevent Strict Mode / HMR double-calls.
156
+ if (isKitIcon && prefixMatch) {
157
+ const kitId = (() => {
158
+ const kits = window.CopyIconsKit || {};
159
+ for (const kId in kits) {
160
+ if (kits[kId].config && kits[kId].config.prefix === prefixMatch[1]) return kId;
161
+ }
162
+ return prefixMatch[1]; // fallback to prefix as key
163
+ })();
164
+
165
+ if (!_isQueued(kitId, prefixMatch[2])) {
166
+ if (el._u) {
167
+ el._u();
287
168
  }
288
- }
289
- if (!isKitIcon && el._u) el._u();
290
- if (!isKitIcon && window.CopyIcons && window.CopyIcons.forceProcess) {
291
- window.CopyIcons.forceProcess(el);
292
169
  }
293
170
  }
294
171
 
295
- document.addEventListener('viconic:ready', tryInjectFromEvent);
296
-
297
- // --- PATH 5: Immediate queue for missing kit icons ---
298
- // Call _queueKitMiss immediately (no per-component delay) so all icons from
299
- // the same render cycle are collected into the same debounced batch request.
300
- if (!isInjected() && isKitIcon && prefixMatch) {
301
- const prefix = prefixMatch[1];
302
- const iconBaseName = prefixMatch[2];
303
- _queueKitMiss(prefix, iconBaseName);
172
+ // PATH 4: Listen for viconic:ready to inject when loader finishes fetching
173
+ function onReady() {
174
+ if (tryInjectFromCache()) {
175
+ document.removeEventListener('viconic:ready', onReady);
176
+ }
304
177
  }
178
+ document.addEventListener('viconic:ready', onReady);
305
179
 
306
180
  return () => {
307
- document.removeEventListener('viconic:ready', tryInjectFromEvent);
181
+ document.removeEventListener('viconic:ready', onReady);
308
182
  };
309
183
  }, [name, className, size, color, style]);
310
184
 
311
185
  const combinedStyle = {
312
186
  ...(size ? { fontSize: size } : {}),
313
187
  ...(color ? { color: color } : {}),
314
- ...style
188
+ ...style,
315
189
  };
316
190
 
317
191
  return (