5 Commits

Author SHA1 Message Date
pheralb b47a7c59b2 🛠️ Add `save-exact` option for consistent dependency versions
📦 Build / 🛠️ Build app (push) Has been cancelled
🧑‍🚀 Check / ⚡ Testing with Vitest (push) Has been cancelled
🧑‍🚀 Check / 📦 SVGs Size (push) Has been cancelled
🚀 Deploy / ☁️ API (push) Has been cancelled
2025-07-14 12:08:51 +01:00
pheralb 1cc1ca8b97 📦 Use `svgo` dependency with exact version 2025-07-14 12:08:28 +01:00
pheralb dc8dffc5c9 ⚙️ Trying to fix copy react code 2025-07-14 12:07:55 +01:00
Pablo Hdez 958a7b9725 Merge pull request #696 from ridemountainpig/fix-duplicate-id
⚙️ fix: resolve duplicate id display issue
2025-07-14 10:38:30 +01:00
ridemountainpig 5c6d6651e3 ⚙️ fix: resolve duplicate id display issue 2025-07-14 16:31:57 +08:00
7 changed files with 206 additions and 30 deletions
+1
View File
@@ -1 +1,2 @@
engine-strict=true
save-exact=true
+1
View File
@@ -56,6 +56,7 @@
"remark-gfm": "4.0.1",
"shiki": "3.7.0",
"svelte-sonner": "0.3.28",
"svgo": "4.0.0",
"tailwind-merge": "2.6.0",
"tailwind-variants": "0.3.1"
},
+125
View File
@@ -59,6 +59,9 @@ importers:
svelte-sonner:
specifier: 0.3.28
version: 0.3.28(svelte@4.2.20)
svgo:
specifier: 4.0.0
version: 4.0.0
tailwind-merge:
specifier: 2.6.0
version: 2.6.0
@@ -1100,6 +1103,9 @@ packages:
peerDependencies:
svelte: ^4.0.0 || ^5.0.0
boolbase@1.0.0:
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
brace-expansion@1.1.11:
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
@@ -1190,6 +1196,10 @@ packages:
comma-separated-tokens@2.0.3:
resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==}
commander@11.1.0:
resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==}
engines: {node: '>=16'}
commander@4.1.1:
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
engines: {node: '>= 6'}
@@ -1228,15 +1238,34 @@ packages:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'}
css-select@5.2.2:
resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==}
css-tree@2.2.1:
resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==}
engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'}
css-tree@2.3.1:
resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==}
engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
css-tree@3.1.0:
resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==}
engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
css-what@6.2.2:
resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==}
engines: {node: '>= 6'}
cssesc@3.0.0:
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
engines: {node: '>=4'}
hasBin: true
csso@5.0.5:
resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==}
engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'}
debug@4.4.0:
resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==}
engines: {node: '>=6.0'}
@@ -1276,6 +1305,19 @@ packages:
dlv@1.1.3:
resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
dom-serializer@2.0.0:
resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
domelementtype@2.3.0:
resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
domhandler@5.0.3:
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
engines: {node: '>= 4'}
domutils@3.2.2:
resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==}
dot-case@3.0.4:
resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==}
@@ -1773,9 +1815,15 @@ packages:
mdast-util-to-string@4.0.0:
resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==}
mdn-data@2.0.28:
resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==}
mdn-data@2.0.30:
resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==}
mdn-data@2.12.2:
resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==}
mdsvex@0.12.5:
resolution: {integrity: sha512-JQy8CBbGF1IvpxZTmGJigRiD1s2BKfLKS9xCLPKngjHToY8WMYLZ8WFGRpuR6x4C4bxipSuLm2LctwL2fVXaEQ==}
peerDependencies:
@@ -1930,6 +1978,9 @@ packages:
resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==}
engines: {node: '>=0.10.0'}
nth-check@2.1.1:
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
object-assign@4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
@@ -2271,6 +2322,9 @@ packages:
safe-buffer@5.1.2:
resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
sax@1.4.1:
resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==}
semver@6.3.1:
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
hasBin: true
@@ -2420,6 +2474,11 @@ packages:
svg-parser@2.0.4:
resolution: {integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==}
svgo@4.0.0:
resolution: {integrity: sha512-VvrHQ+9uniE+Mvx3+C9IEe/lWasXCU0nXMY2kZeLrHNICuRiC8uMPyM14UEaMOFA5mhyQqEkB02VoQ16n3DLaw==}
engines: {node: '>=16'}
hasBin: true
tabbable@6.2.0:
resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==}
@@ -3584,6 +3643,8 @@ snapshots:
nanoid: 5.1.2
svelte: 4.2.20
boolbase@1.0.0: {}
brace-expansion@1.1.11:
dependencies:
balanced-match: 1.0.2
@@ -3679,6 +3740,8 @@ snapshots:
comma-separated-tokens@2.0.3: {}
commander@11.1.0: {}
commander@4.1.1: {}
commondir@1.0.1: {}
@@ -3716,13 +3779,37 @@ snapshots:
shebang-command: 2.0.0
which: 2.0.2
css-select@5.2.2:
dependencies:
boolbase: 1.0.0
css-what: 6.2.2
domhandler: 5.0.3
domutils: 3.2.2
nth-check: 2.1.1
css-tree@2.2.1:
dependencies:
mdn-data: 2.0.28
source-map-js: 1.2.1
css-tree@2.3.1:
dependencies:
mdn-data: 2.0.30
source-map-js: 1.2.1
css-tree@3.1.0:
dependencies:
mdn-data: 2.12.2
source-map-js: 1.2.1
css-what@6.2.2: {}
cssesc@3.0.0: {}
csso@5.0.5:
dependencies:
css-tree: 2.2.1
debug@4.4.0:
dependencies:
ms: 2.1.3
@@ -3749,6 +3836,24 @@ snapshots:
dlv@1.1.3: {}
dom-serializer@2.0.0:
dependencies:
domelementtype: 2.3.0
domhandler: 5.0.3
entities: 4.5.0
domelementtype@2.3.0: {}
domhandler@5.0.3:
dependencies:
domelementtype: 2.3.0
domutils@3.2.2:
dependencies:
dom-serializer: 2.0.0
domelementtype: 2.3.0
domhandler: 5.0.3
dot-case@3.0.4:
dependencies:
no-case: 3.0.4
@@ -4372,8 +4477,12 @@ snapshots:
dependencies:
'@types/mdast': 4.0.4
mdn-data@2.0.28: {}
mdn-data@2.0.30: {}
mdn-data@2.12.2: {}
mdsvex@0.12.5(svelte@4.2.20):
dependencies:
'@types/mdast': 4.0.4
@@ -4625,6 +4734,10 @@ snapshots:
normalize-range@0.1.2: {}
nth-check@2.1.1:
dependencies:
boolbase: 1.0.0
object-assign@4.1.1: {}
object-hash@3.0.0: {}
@@ -4946,6 +5059,8 @@ snapshots:
safe-buffer@5.1.2: {}
sax@1.4.1: {}
semver@6.3.1: {}
semver@7.7.1: {}
@@ -5111,6 +5226,16 @@ snapshots:
svg-parser@2.0.4: {}
svgo@4.0.0:
dependencies:
commander: 11.1.0
css-select: 5.2.2
css-tree: 3.1.0
css-what: 6.2.2
csso: 5.0.5
picocolors: 1.1.1
sax: 1.4.1
tabbable@6.2.0: {}
tailwind-merge@2.5.4: {}
+37 -7
View File
@@ -12,6 +12,7 @@
import { cn } from '@/utils/cn';
import { clipboard } from '@/utils/clipboard';
import { copyToClipboard as figmaCopyToClipboard } from '@/figma/copy-to-clipboard';
import { getPrefixFromSvgUrl, prefixSvgIds } from '@/utils/prefixSvgIds';
// Templates:
import { getSource } from '@/templates/getSource';
@@ -84,10 +85,14 @@
const svgUrlToCopy = getSvgUrl();
optionsOpen = false;
const content = await getSource({
let content = await getSource({
url: svgUrlToCopy
});
if (svgUrlToCopy) {
content = prefixSvgIds(content, getPrefixFromSvgUrl(svgUrlToCopy));
}
if (isInFigma) {
figmaCopyToClipboard(content);
}
@@ -125,9 +130,14 @@
isLoading = true;
const title = svgInfo.title.split(' ').join('');
const content = await getSource({
let content = await getSource({
url: svgUrlToCopy
});
if (svgUrlToCopy) {
content = prefixSvgIds(content, getPrefixFromSvgUrl(svgUrlToCopy));
}
const dataComponent = { code: content, typescript: tsx, name: title };
const { data, error } = await getReactCode(dataComponent);
@@ -156,10 +166,14 @@
optionsOpen = false;
const content = await getSource({
let content = await getSource({
url: svgUrlToCopy
});
if (svgUrlToCopy) {
content = prefixSvgIds(content, getPrefixFromSvgUrl(svgUrlToCopy));
}
const copyCode = getVueCode({
content: content,
lang: ts ? 'ts' : 'js'
@@ -189,10 +203,14 @@
optionsOpen = false;
const content = await getSource({
let content = await getSource({
url: svgUrlToCopy
});
if (svgUrlToCopy) {
content = prefixSvgIds(content, getPrefixFromSvgUrl(svgUrlToCopy));
}
const copyCode = getSvelteCode({
content: content,
lang: ts ? 'ts' : 'js'
@@ -222,10 +240,14 @@
const title = svgInfo.title.split(' ').join('');
const svgUrlToCopy = getSvgUrl();
const content = await getSource({
let content = await getSource({
url: svgUrlToCopy
});
if (svgUrlToCopy) {
content = prefixSvgIds(content, getPrefixFromSvgUrl(svgUrlToCopy));
}
if (!content) {
toast.error('Failed to fetch the SVG content', {
duration: 5000
@@ -255,10 +277,14 @@
const title = svgInfo.title.split(' ').join('');
const svgUrlToCopy = getSvgUrl();
const content = await getSource({
let content = await getSource({
url: svgUrlToCopy
});
if (svgUrlToCopy) {
content = prefixSvgIds(content, getPrefixFromSvgUrl(svgUrlToCopy));
}
if (!content) {
toast.error('Failed to fetch the SVG content', {
duration: 5000
@@ -287,10 +313,14 @@
optionsOpen = false;
const svgUrlToCopy = getSvgUrl();
const content = await getSource({
let content = await getSource({
url: svgUrlToCopy
});
if (svgUrlToCopy) {
content = prefixSvgIds(content, getPrefixFromSvgUrl(svgUrlToCopy));
}
if (!content) {
toast.error('Failed to fetch the SVG content', {
duration: 5000
+20 -4
View File
@@ -17,6 +17,7 @@
} from '@/ui/dialog';
import { buttonStyles } from '@/ui/styles';
import { cn } from '@/utils/cn';
import { getPrefixFromSvgUrl, prefixSvgIds } from '@/utils/prefixSvgIds';
// Props:
export let svgInfo: iSVG;
@@ -31,8 +32,14 @@
'flex w-full h-full flex-col p-4 rounded-md shadow-sm dark:bg-neutral-800/20 bg-neutral-200/10 border border-neutral-200 dark:border-neutral-800 space-y-2';
// Functions:
const downloadSvg = (url?: string) => {
download(url || '');
const downloadSvg = async (url?: string) => {
let content = await getSource({
url: url
});
if (url) {
content = prefixSvgIds(content, getPrefixFromSvgUrl(url));
}
download(content || '', url?.split('/').pop() || '', 'image/svg+xml');
const category = Array.isArray(svgInfo.category)
? svgInfo.category.sort().join(' - ')
@@ -55,13 +62,22 @@
}) => {
const zip = new JSZip();
const lightSvg = await getSource({
let lightSvg = await getSource({
url: lightRoute
});
const darkSvg = await getSource({
let darkSvg = await getSource({
url: darkRoute
});
lightSvg = prefixSvgIds(
lightSvg,
svgInfo.title.toLowerCase() + (isWordmark ? '_wordmark_light' : '_light')
);
darkSvg = prefixSvgIds(
darkSvg,
svgInfo.title.toLowerCase() + (isWordmark ? '_wordmark_dark' : '_dark')
);
if (isWordmark) {
zip.file(`${svgInfo.title}_wordmark_light.svg`, lightSvg);
zip.file(`${svgInfo.title}_wordmark_dark.svg`, darkSvg);
+1 -19
View File
@@ -13,21 +13,6 @@ export const GET = async () => {
};
export const POST = async ({ request }: RequestEvent) => {
const ip = request.headers.get('x-forwarded-for') ?? '';
const { success, reset } = await ratelimit.limit(ip);
// Error 429 | If rate limit is exceeded:
if (!success) {
const now = Date.now();
const retryAfter = Math.floor((reset - now) / 500);
return new Response('Too Many Requests', {
status: 429,
headers: {
'Retry-After': retryAfter.toString()
}
});
}
try {
const body = await request.json();
@@ -47,9 +32,6 @@ export const POST = async ({ request }: RequestEvent) => {
return json({ data: jsCode }, { status: 200 });
} catch (error) {
return json(
{ error: `Error al transformar el SVG a componente React: ${error}` },
{ status: 500 }
);
return json({ error: `⚠️ api/svgs/svgr - Error: ${error}` }, { status: 500 });
}
};
+21
View File
@@ -0,0 +1,21 @@
import { optimize } from 'svgo';
export const getPrefixFromSvgUrl = (svgUrl: string) => {
return svgUrl.split('/').pop()!.replace('.svg', '').split('-').join('_');
};
export const prefixSvgIds = (content: string, prefix: string): string => {
const result = optimize(content, {
plugins: [
{
name: 'prefixIds',
params: {
prefix
}
}
],
multipass: false
});
return (result as { data: string }).data;
};