viconic-react-icons 1.5.6 → 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,101 +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
- const cdnFetches = [];
2056
- for (const n in bj.icons) {
2057
- if (!Object.prototype.hasOwnProperty.call(bj.icons, n)) continue;
2058
- const d = bj.icons[n];
2059
- const svg = d.svg || "";
2060
- if (svg && svg.includes("<")) {
2061
- W.icons[`@${prefix}/${n}`] = svg;
2062
- W.icons[`${prefix}:${n}`] = svg;
2063
- W.icons[`${prefix}/${n}`] = svg;
2064
- W.icons[n] = W.icons[n] || svg;
2065
- } else if (d.cdn_url) {
2066
- cdnFetches.push(
2067
- fetch(d.cdn_url).then((r) => r.ok ? r.text() : "").then((s) => {
2068
- if (s && s.includes("<")) {
2069
- W.icons[`@${prefix}/${n}`] = s;
2070
- W.icons[`${prefix}:${n}`] = s;
2071
- W.icons[`${prefix}/${n}`] = s;
2072
- W.icons[n] = W.icons[n] || s;
2073
- }
2074
- }).catch(() => {
2075
- })
2076
- );
2077
- }
2078
- }
2079
- Promise.all(cdnFetches).then(() => {
2080
- try {
2081
- document.dispatchEvent(new CustomEvent("viconic:ready", { detail: { prefix, kitId } }));
2082
- } catch (e) {
2083
- }
2084
- });
2085
- }).catch(() => {
2086
- });
2087
- }, 150);
2088
- }
2089
- function _queueKitMiss(prefix, iconBaseName) {
2090
- if (typeof window === "undefined") return;
2091
- const entry = _resolveRegistry(prefix);
2092
- if (entry) {
2093
- _dispatchBatchFetch(prefix, iconBaseName, entry);
2094
- } else {
2095
- if (!_pendingMisses[prefix]) _pendingMisses[prefix] = /* @__PURE__ */ new Set();
2096
- _pendingMisses[prefix].add(iconBaseName);
2097
- }
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;
2098
2012
  }
2099
2013
  function initViconic(options = {}) {
2100
- const { kitId, cdnBase = "cdn.viconic.dev", version, prefix: explicitPrefix, api = "https://api.viconic.dev" } = options;
2014
+ const { kitId, cdnBase = "cdn.viconic.dev", version } = options;
2101
2015
  if (typeof window === "undefined") return;
2102
2016
  if (!kitId) {
2103
2017
  console.warn("[Viconic] initViconic requires a kitId");
@@ -2105,31 +2019,14 @@ function initViconic(options = {}) {
2105
2019
  }
2106
2020
  if (_initializedKits.has(kitId)) return;
2107
2021
  _initializedKits.add(kitId);
2108
- if (explicitPrefix) {
2109
- _kitRegistry[explicitPrefix] = { kitId, api };
2110
- }
2111
- if (typeof window !== "undefined") {
2112
- window.__viconicKitIds = window.__viconicKitIds || {};
2113
- window.__viconicKitIds[kitId] = { api };
2114
- }
2115
2022
  const scriptId = `viconic-kit-${kitId}`;
2116
2023
  if (document.getElementById(scriptId)) return;
2117
2024
  const script = document.createElement("script");
2118
2025
  script.id = scriptId;
2119
2026
  let url = `https://${cdnBase}/kits/${kitId}/loader.js`;
2120
2027
  if (version) {
2121
- const vQuery = version === "dev" ? Date.now() : version;
2122
- url += `?v=${vQuery}`;
2028
+ url += `?v=${version === "dev" ? Date.now() : version}`;
2123
2029
  }
2124
- script.onload = () => {
2125
- const kits = window.CopyIconsKit || {};
2126
- const kit = kits[kitId];
2127
- if (kit && kit.config && kit.config.prefix) {
2128
- const pfx = kit.config.prefix;
2129
- _kitRegistry[pfx] = { kitId, api: kit.config.api || api };
2130
- _flushPendingMisses(pfx);
2131
- }
2132
- };
2133
2030
  script.src = url;
2134
2031
  script.async = true;
2135
2032
  document.head.appendChild(script);
@@ -2150,51 +2047,50 @@ var ViconicIcon = ({ name, className, style, size, color, ...props }) => {
2150
2047
  function isInjected() {
2151
2048
  return el.classList.contains("vi-ok") || el.classList.contains("svg-loaded");
2152
2049
  }
2153
- if (!isInjected()) {
2050
+ function tryInjectFromCache() {
2051
+ if (isInjected()) return false;
2154
2052
  const W = window.__viconic;
2155
- if (W && W.icons) {
2156
- const iconBaseName = prefixMatch ? prefixMatch[2] : null;
2157
- const svg = iconBaseName && W.icons[iconBaseName] || name && W.icons[name] || null;
2158
- if (svg) {
2159
- el.innerHTML = svg;
2160
- el.classList.add("vi-ok", "svg-loaded");
2161
- }
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;
2162
2060
  }
2061
+ return false;
2163
2062
  }
2164
- if (!isInjected() && !isKitIcon) {
2063
+ if (tryInjectFromCache()) return;
2064
+ if (!isKitIcon) {
2165
2065
  if (window.CopyIcons && window.CopyIcons.forceProcess) {
2166
2066
  window.CopyIcons.forceProcess(el);
2067
+ } else if (el._u) {
2068
+ el._u();
2167
2069
  }
2168
2070
  }
2169
- if (!isInjected() && !isKitIcon && el._u) {
2170
- el._u();
2171
- }
2172
2071
  if (isInjected()) return;
2173
- function tryInjectFromEvent() {
2174
- if (isInjected()) return;
2175
- const W = window.__viconic;
2176
- if (W && W.icons) {
2177
- const iconBaseName = prefixMatch ? prefixMatch[2] : null;
2178
- const svg = iconBaseName && W.icons[iconBaseName] || name && W.icons[name] || null;
2179
- if (svg) {
2180
- el.innerHTML = svg;
2181
- el.classList.add("vi-ok", "svg-loaded");
2182
- 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();
2183
2083
  }
2184
- }
2185
- if (!isKitIcon && el._u) el._u();
2186
- if (!isKitIcon && window.CopyIcons && window.CopyIcons.forceProcess) {
2187
- window.CopyIcons.forceProcess(el);
2188
2084
  }
2189
2085
  }
2190
- document.addEventListener("viconic:ready", tryInjectFromEvent);
2191
- if (!isInjected() && isKitIcon && prefixMatch) {
2192
- const prefix = prefixMatch[1];
2193
- const iconBaseName = prefixMatch[2];
2194
- _queueKitMiss(prefix, iconBaseName);
2086
+ function onReady() {
2087
+ if (tryInjectFromCache()) {
2088
+ document.removeEventListener("viconic:ready", onReady);
2089
+ }
2195
2090
  }
2091
+ document.addEventListener("viconic:ready", onReady);
2196
2092
  return () => {
2197
- document.removeEventListener("viconic:ready", tryInjectFromEvent);
2093
+ document.removeEventListener("viconic:ready", onReady);
2198
2094
  };
2199
2095
  }, [name, className, size, color, style]);
2200
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,101 +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
- const cdnFetches = [];
2021
- for (const n in bj.icons) {
2022
- if (!Object.prototype.hasOwnProperty.call(bj.icons, n)) continue;
2023
- const d = bj.icons[n];
2024
- const svg = d.svg || "";
2025
- if (svg && svg.includes("<")) {
2026
- W.icons[`@${prefix}/${n}`] = svg;
2027
- W.icons[`${prefix}:${n}`] = svg;
2028
- W.icons[`${prefix}/${n}`] = svg;
2029
- W.icons[n] = W.icons[n] || svg;
2030
- } else if (d.cdn_url) {
2031
- cdnFetches.push(
2032
- fetch(d.cdn_url).then((r) => r.ok ? r.text() : "").then((s) => {
2033
- if (s && s.includes("<")) {
2034
- W.icons[`@${prefix}/${n}`] = s;
2035
- W.icons[`${prefix}:${n}`] = s;
2036
- W.icons[`${prefix}/${n}`] = s;
2037
- W.icons[n] = W.icons[n] || s;
2038
- }
2039
- }).catch(() => {
2040
- })
2041
- );
2042
- }
2043
- }
2044
- Promise.all(cdnFetches).then(() => {
2045
- try {
2046
- document.dispatchEvent(new CustomEvent("viconic:ready", { detail: { prefix, kitId } }));
2047
- } catch (e) {
2048
- }
2049
- });
2050
- }).catch(() => {
2051
- });
2052
- }, 150);
2053
- }
2054
- function _queueKitMiss(prefix, iconBaseName) {
2055
- if (typeof window === "undefined") return;
2056
- const entry = _resolveRegistry(prefix);
2057
- if (entry) {
2058
- _dispatchBatchFetch(prefix, iconBaseName, entry);
2059
- } else {
2060
- if (!_pendingMisses[prefix]) _pendingMisses[prefix] = /* @__PURE__ */ new Set();
2061
- _pendingMisses[prefix].add(iconBaseName);
2062
- }
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;
2063
1977
  }
2064
1978
  function initViconic(options = {}) {
2065
- const { kitId, cdnBase = "cdn.viconic.dev", version, prefix: explicitPrefix, api = "https://api.viconic.dev" } = options;
1979
+ const { kitId, cdnBase = "cdn.viconic.dev", version } = options;
2066
1980
  if (typeof window === "undefined") return;
2067
1981
  if (!kitId) {
2068
1982
  console.warn("[Viconic] initViconic requires a kitId");
@@ -2070,31 +1984,14 @@ function initViconic(options = {}) {
2070
1984
  }
2071
1985
  if (_initializedKits.has(kitId)) return;
2072
1986
  _initializedKits.add(kitId);
2073
- if (explicitPrefix) {
2074
- _kitRegistry[explicitPrefix] = { kitId, api };
2075
- }
2076
- if (typeof window !== "undefined") {
2077
- window.__viconicKitIds = window.__viconicKitIds || {};
2078
- window.__viconicKitIds[kitId] = { api };
2079
- }
2080
1987
  const scriptId = `viconic-kit-${kitId}`;
2081
1988
  if (document.getElementById(scriptId)) return;
2082
1989
  const script = document.createElement("script");
2083
1990
  script.id = scriptId;
2084
1991
  let url = `https://${cdnBase}/kits/${kitId}/loader.js`;
2085
1992
  if (version) {
2086
- const vQuery = version === "dev" ? Date.now() : version;
2087
- url += `?v=${vQuery}`;
1993
+ url += `?v=${version === "dev" ? Date.now() : version}`;
2088
1994
  }
2089
- script.onload = () => {
2090
- const kits = window.CopyIconsKit || {};
2091
- const kit = kits[kitId];
2092
- if (kit && kit.config && kit.config.prefix) {
2093
- const pfx = kit.config.prefix;
2094
- _kitRegistry[pfx] = { kitId, api: kit.config.api || api };
2095
- _flushPendingMisses(pfx);
2096
- }
2097
- };
2098
1995
  script.src = url;
2099
1996
  script.async = true;
2100
1997
  document.head.appendChild(script);
@@ -2115,51 +2012,50 @@ var ViconicIcon = ({ name, className, style, size, color, ...props }) => {
2115
2012
  function isInjected() {
2116
2013
  return el.classList.contains("vi-ok") || el.classList.contains("svg-loaded");
2117
2014
  }
2118
- if (!isInjected()) {
2015
+ function tryInjectFromCache() {
2016
+ if (isInjected()) return false;
2119
2017
  const W = window.__viconic;
2120
- if (W && W.icons) {
2121
- const iconBaseName = prefixMatch ? prefixMatch[2] : null;
2122
- const svg = iconBaseName && W.icons[iconBaseName] || name && W.icons[name] || null;
2123
- if (svg) {
2124
- el.innerHTML = svg;
2125
- el.classList.add("vi-ok", "svg-loaded");
2126
- }
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;
2127
2025
  }
2026
+ return false;
2128
2027
  }
2129
- if (!isInjected() && !isKitIcon) {
2028
+ if (tryInjectFromCache()) return;
2029
+ if (!isKitIcon) {
2130
2030
  if (window.CopyIcons && window.CopyIcons.forceProcess) {
2131
2031
  window.CopyIcons.forceProcess(el);
2032
+ } else if (el._u) {
2033
+ el._u();
2132
2034
  }
2133
2035
  }
2134
- if (!isInjected() && !isKitIcon && el._u) {
2135
- el._u();
2136
- }
2137
2036
  if (isInjected()) return;
2138
- function tryInjectFromEvent() {
2139
- if (isInjected()) return;
2140
- const W = window.__viconic;
2141
- if (W && W.icons) {
2142
- const iconBaseName = prefixMatch ? prefixMatch[2] : null;
2143
- const svg = iconBaseName && W.icons[iconBaseName] || name && W.icons[name] || null;
2144
- if (svg) {
2145
- el.innerHTML = svg;
2146
- el.classList.add("vi-ok", "svg-loaded");
2147
- 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();
2148
2048
  }
2149
- }
2150
- if (!isKitIcon && el._u) el._u();
2151
- if (!isKitIcon && window.CopyIcons && window.CopyIcons.forceProcess) {
2152
- window.CopyIcons.forceProcess(el);
2153
2049
  }
2154
2050
  }
2155
- document.addEventListener("viconic:ready", tryInjectFromEvent);
2156
- if (!isInjected() && isKitIcon && prefixMatch) {
2157
- const prefix = prefixMatch[1];
2158
- const iconBaseName = prefixMatch[2];
2159
- _queueKitMiss(prefix, iconBaseName);
2051
+ function onReady() {
2052
+ if (tryInjectFromCache()) {
2053
+ document.removeEventListener("viconic:ready", onReady);
2054
+ }
2160
2055
  }
2056
+ document.addEventListener("viconic:ready", onReady);
2161
2057
  return () => {
2162
- document.removeEventListener("viconic:ready", tryInjectFromEvent);
2058
+ document.removeEventListener("viconic:ready", onReady);
2163
2059
  };
2164
2060
  }, [name, className, size, color, style]);
2165
2061
  const combinedStyle = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "viconic-react-icons",
3
- "version": "1.5.6",
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,189 +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
- const cdnFetches = [];
113
- for (const n in bj.icons) {
114
- if (!Object.prototype.hasOwnProperty.call(bj.icons, n)) continue;
115
- const d = bj.icons[n];
116
- const svg = d.svg || '';
117
- if (svg && svg.includes('<')) {
118
- W.icons[`@${prefix}/${n}`] = svg;
119
- W.icons[`${prefix}:${n}`] = svg;
120
- W.icons[`${prefix}/${n}`] = svg;
121
- W.icons[n] = W.icons[n] || svg;
122
- } else if (d.cdn_url) {
123
- // User-uploaded icon: fetch SVG from CDN and store in W.icons
124
- // so loader refresh() finds it and doesn't fetch individually
125
- cdnFetches.push(
126
- fetch(d.cdn_url)
127
- .then(r => r.ok ? r.text() : '')
128
- .then(s => {
129
- if (s && s.includes('<')) {
130
- W.icons[`@${prefix}/${n}`] = s;
131
- W.icons[`${prefix}:${n}`] = s;
132
- W.icons[`${prefix}/${n}`] = s;
133
- W.icons[n] = W.icons[n] || s;
134
- }
135
- })
136
- .catch(() => {})
137
- );
138
- }
139
- }
140
- Promise.all(cdnFetches).then(() => {
141
- try { document.dispatchEvent(new CustomEvent('viconic:ready', { detail: { prefix, kitId } })); } catch(e) {}
142
- });
143
- })
144
- .catch(() => {});
145
- }, 150);
146
- }
147
-
148
- function _queueKitMiss(prefix, iconBaseName) {
47
+ function _clearQueued(kitId) {
149
48
  if (typeof window === 'undefined') return;
150
- const entry = _resolveRegistry(prefix);
151
- if (entry) {
152
- _dispatchBatchFetch(prefix, iconBaseName, entry);
153
- } else {
154
- // Registry not ready yet — buffer until loader fires
155
- if (!_pendingMisses[prefix]) _pendingMisses[prefix] = new Set();
156
- _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];
157
54
  }
158
55
  }
159
56
 
160
57
  /**
161
58
  * Initialize a Viconic Kit in your React app.
162
59
  * Injects the kit's loader.js script into <head>.
163
- * Supports multiple kits — call once per kit.
164
60
  *
165
61
  * @param {Object} options
166
62
  * @param {string} options.kitId - UUID of the Kit (required)
167
63
  * @param {string} [options.cdnBase] - Custom CDN domain (default: cdn.viconic.dev)
168
- *
169
- * @example
170
- * import { initViconic } from 'viconic-react-icons';
171
- *
172
- * initViconic({ kitId: '387a6161-cb39-411f-8f13-29a5813e4efd' });
173
- * // Multiple kits:
174
- * initViconic({ kitId: 'another-kit-uuid', version: '1.0' });
64
+ * @param {string} [options.version] - Version string for cache busting
175
65
  */
176
66
  export function initViconic(options = {}) {
177
- const { kitId, cdnBase = 'cdn.viconic.dev', version, prefix: explicitPrefix, api = 'https://api.viconic.dev' } = options;
178
- if (typeof window === 'undefined') return; // SSR guard
67
+ const { kitId, cdnBase = 'cdn.viconic.dev', version } = options;
68
+ if (typeof window === 'undefined') return;
179
69
  if (!kitId) {
180
70
  console.warn('[Viconic] initViconic requires a kitId');
181
71
  return;
182
72
  }
183
- if (_initializedKits.has(kitId)) return; // Already initialized
73
+ if (_initializedKits.has(kitId)) return;
184
74
  _initializedKits.add(kitId);
185
75
 
186
- // Register synchronously so _queueKitMiss can find kitId immediately,
187
- // even before the async loader.js script has loaded and populated CopyIconsKit.
188
- // If prefix is not known yet, we'll also register once CopyIconsKit fires.
189
- if (explicitPrefix) {
190
- _kitRegistry[explicitPrefix] = { kitId, api };
191
- }
192
- // Also register by kitId so we can backfill prefix once loader fires
193
- if (typeof window !== 'undefined') {
194
- window.__viconicKitIds = window.__viconicKitIds || {};
195
- window.__viconicKitIds[kitId] = { api };
196
- }
197
-
198
76
  const scriptId = `viconic-kit-${kitId}`;
199
- if (document.getElementById(scriptId)) return; // Script already in DOM
77
+ if (document.getElementById(scriptId)) return;
200
78
 
201
79
  const script = document.createElement('script');
202
80
  script.id = scriptId;
203
81
 
204
- // Add cache busting for development or specific version updates
205
82
  let url = `https://${cdnBase}/kits/${kitId}/loader.js`;
206
83
  if (version) {
207
- const vQuery = version === 'dev' ? Date.now() : version;
208
- url += `?v=${vQuery}`;
84
+ url += `?v=${version === 'dev' ? Date.now() : version}`;
209
85
  }
210
86
 
211
- // Once loader.js runs and registers CopyIconsKit, backfill _kitRegistry and flush pending misses
212
- script.onload = () => {
213
- const kits = window.CopyIconsKit || {};
214
- const kit = kits[kitId];
215
- if (kit && kit.config && kit.config.prefix) {
216
- const pfx = kit.config.prefix;
217
- _kitRegistry[pfx] = { kitId, api: kit.config.api || api };
218
- // Flush any misses buffered before the loader was ready
219
- _flushPendingMisses(pfx);
220
- }
221
- };
222
-
223
87
  script.src = url;
224
88
  script.async = true;
225
89
  document.head.appendChild(script);
@@ -227,11 +91,7 @@ export function initViconic(options = {}) {
227
91
 
228
92
  /**
229
93
  * ViconicIcon — React component for rendering Viconic icons.
230
- *
231
- * Works with both:
232
- * - System icons: <ViconicIcon name="lucide:home" />
233
- * - Kit icons: <ViconicIcon name="@myprefix/home" /> (after initViconic)
234
- *
94
+ *
235
95
  * @param {string} name - Icon identifier (e.g., "lucide:home", "@prefix/name")
236
96
  * @param {string} [className] - CSS class names
237
97
  * @param {object} [style] - Inline styles
@@ -246,7 +106,6 @@ export const ViconicIcon = ({ name, className, style, size, color, ...props }) =
246
106
  const el = iconRef.current;
247
107
  if (!el) return;
248
108
 
249
- // --- FIX DOM REUSE (Reconciliation) ---
250
109
  if (prevNameRef.current !== name) {
251
110
  el.innerHTML = '';
252
111
  el.classList.remove('vi-ok', 'svg-loaded', 'vi-mono', 'ci-multicolor');
@@ -260,78 +119,73 @@ export const ViconicIcon = ({ name, className, style, size, color, ...props }) =
260
119
  return el.classList.contains('vi-ok') || el.classList.contains('svg-loaded');
261
120
  }
262
121
 
263
- // --- PATH 1: Synchronous inject from W.icons (pre-loaded from localStorage at module init) ---
264
- if (!isInjected()) {
122
+ function tryInjectFromCache() {
123
+ if (isInjected()) return false;
265
124
  const W = window.__viconic;
266
- if (W && W.icons) {
267
- // Try all key variants: iconBaseName, @prefix/name, prefix:name, prefix/name
268
- const iconBaseName = prefixMatch ? prefixMatch[2] : null;
269
- const svg = (iconBaseName && W.icons[iconBaseName])
270
- || (name && W.icons[name])
271
- || null;
272
- if (svg) {
273
- el.innerHTML = svg;
274
- el.classList.add('vi-ok', 'svg-loaded');
275
- }
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;
276
134
  }
135
+ return false;
277
136
  }
278
137
 
279
- // --- PATH 2: Smart loader for system icons (prefix:name) ---
280
- 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) {
281
143
  if (window.CopyIcons && window.CopyIcons.forceProcess) {
282
144
  window.CopyIcons.forceProcess(el);
145
+ } else if (el._u) {
146
+ el._u();
283
147
  }
284
148
  }
285
149
 
286
- // --- PATH 3: Custom element _u() call (kit loader registered element, system icons only) ---
287
- if (!isInjected() && !isKitIcon && el._u) {
288
- el._u();
289
- }
290
-
291
150
  if (isInjected()) return;
292
151
 
293
- // --- PATH 4: Event-drivenlisten for viconic:ready fired by kit loader after register() ---
294
- // Much faster than polling: icons appear the instant the loader finishes fetching them.
295
- function tryInjectFromEvent() {
296
- if (isInjected()) return;
297
- const W = window.__viconic;
298
- if (W && W.icons) {
299
- const iconBaseName = prefixMatch ? prefixMatch[2] : null;
300
- const svg = (iconBaseName && W.icons[iconBaseName])
301
- || (name && W.icons[name])
302
- || null;
303
- if (svg) {
304
- el.innerHTML = svg;
305
- el.classList.add('vi-ok', 'svg-loaded');
306
- 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();
307
168
  }
308
- }
309
- if (!isKitIcon && el._u) el._u();
310
- if (!isKitIcon && window.CopyIcons && window.CopyIcons.forceProcess) {
311
- window.CopyIcons.forceProcess(el);
312
169
  }
313
170
  }
314
171
 
315
- document.addEventListener('viconic:ready', tryInjectFromEvent);
316
-
317
- // --- PATH 5: Immediate queue for missing kit icons ---
318
- // Call _queueKitMiss immediately (no per-component delay) so all icons from
319
- // the same render cycle are collected into the same debounced batch request.
320
- if (!isInjected() && isKitIcon && prefixMatch) {
321
- const prefix = prefixMatch[1];
322
- const iconBaseName = prefixMatch[2];
323
- _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
+ }
324
177
  }
178
+ document.addEventListener('viconic:ready', onReady);
325
179
 
326
180
  return () => {
327
- document.removeEventListener('viconic:ready', tryInjectFromEvent);
181
+ document.removeEventListener('viconic:ready', onReady);
328
182
  };
329
183
  }, [name, className, size, color, style]);
330
184
 
331
185
  const combinedStyle = {
332
186
  ...(size ? { fontSize: size } : {}),
333
187
  ...(color ? { color: color } : {}),
334
- ...style
188
+ ...style,
335
189
  };
336
190
 
337
191
  return (