From 79ce39ad80d157a7e67874228fbc165e1d59ad0d Mon Sep 17 00:00:00 2001 From: piuclaw Date: Mon, 6 Jul 2026 15:09:27 -0300 Subject: [PATCH 1/2] fix: improve error handling in SVG download and copy functionality - getSource.ts: Add response.ok check to prevent processing error pages as valid SVG - downloadSvg.svelte: Remove redundant ternary expressions that always returned same value - /api/svgs/svgr/+server.ts: Add input validation for code and name fields - copySvg.svelte: Add try/catch to React, Angular, Web, and Astro converters to reset loading state on error --- src/components/svgs/copySvg.svelte | 244 ++++++++++++++----------- src/components/svgs/downloadSvg.svelte | 4 +- src/routes/api/svgs/svgr/+server.ts | 4 + src/templates/getSource.ts | 6 +- 4 files changed, 145 insertions(+), 113 deletions(-) diff --git a/src/components/svgs/copySvg.svelte b/src/components/svgs/copySvg.svelte index ab1647869..c990db473 100644 --- a/src/components/svgs/copySvg.svelte +++ b/src/components/svgs/copySvg.svelte @@ -147,45 +147,51 @@ // Convert SVG as React component: const convertSvgReactComponent = async (tsx: boolean) => { - const svgUrlToCopy = getSvgUrl(); - optionsOpen = false; + try { + const svgUrlToCopy = getSvgUrl(); + optionsOpen = false; - isLoading = true; + isLoading = true; - const title = svgInfo.title.split(" ").join(""); - let content = await getSource({ - url: svgUrlToCopy, - optimize, - }); + const title = svgInfo.title.split(" ").join(""); + let content = await getSource({ + url: svgUrlToCopy, + optimize, + }); - if (svgUrlToCopy) { - content = prefixSvgIds(content, getPrefixFromSvgUrl(svgUrlToCopy)); - } + if (svgUrlToCopy) { + content = prefixSvgIds(content, getPrefixFromSvgUrl(svgUrlToCopy)); + } - const dataComponent = { - code: content, - typescript: tsx, - name: title, - optimize, - }; - const { data, error } = await getReactCode(dataComponent); + const dataComponent = { + code: content, + typescript: tsx, + name: title, + optimize, + }; + const { data, error } = await getReactCode(dataComponent); + + if (error || !data) { + toast.error("Failed to fetch React component", { + description: `${error ?? ""}`, + duration: 5000, + }); + isLoading = false; + return; + } - if (error || !data) { - toast.error("Failed to fetch React component", { - description: `${error ?? ""}`, - duration: 5000, + await clipboard(data); + + toast.success(`Copied as React ${tsx ? "TSX" : "JSX"} component`, { + description: `${svgInfo.title} - ${svgInfo.category}`, }); + + isLoading = false; + } catch (err) { + console.error("Error converting to React component:", err); + toast.error("Failed to convert React component"); isLoading = false; - return; } - - await clipboard(data); - - toast.success(`Copied as React ${tsx ? "TSX" : "JSX"} component`, { - description: `${svgInfo.title} - ${svgInfo.category}`, - }); - - isLoading = false; }; // Copy SVG as Vue Component: @@ -266,114 +272,132 @@ // Copy SVG as Standalone Angular component: const convertSvgAngularComponent = async () => { - isLoading = true; - optionsOpen = false; + try { + isLoading = true; + optionsOpen = false; - const title = svgInfo.title.split(" ").join(""); - const svgUrlToCopy = getSvgUrl(); - let content = await getSource({ - url: svgUrlToCopy, - optimize, - }); + const title = svgInfo.title.split(" ").join(""); + const svgUrlToCopy = getSvgUrl(); + let content = await getSource({ + url: svgUrlToCopy, + optimize, + }); - if (svgUrlToCopy) { - content = prefixSvgIds(content, getPrefixFromSvgUrl(svgUrlToCopy)); - } + if (svgUrlToCopy) { + content = prefixSvgIds(content, getPrefixFromSvgUrl(svgUrlToCopy)); + } - if (!content) { - toast.error("Failed to fetch the SVG content", { - duration: 5000, - }); - isLoading = false; - return; - } + if (!content) { + toast.error("Failed to fetch the SVG content", { + duration: 5000, + }); + isLoading = false; + return; + } - const angularComponent = getAngularCode({ - componentName: title, - svgContent: content, - }); + const angularComponent = getAngularCode({ + componentName: title, + svgContent: content, + }); - await clipboard(angularComponent); + await clipboard(angularComponent); - toast.success(`Copied as Standalone Angular component`, { - description: `${svgInfo.title} - ${svgInfo.category}`, - }); + toast.success(`Copied as Standalone Angular component`, { + description: `${svgInfo.title} - ${svgInfo.category}`, + }); - isLoading = false; + isLoading = false; + } catch (err) { + console.error("Error converting to Angular component:", err); + toast.error("Failed to convert Angular component"); + isLoading = false; + } }; // Copy SVG as Web Component: const convertSvgWebComponent = async () => { - isLoading = true; - optionsOpen = false; + try { + isLoading = true; + optionsOpen = false; - const title = svgInfo.title.split(" ").join(""); - const svgUrlToCopy = getSvgUrl(); - let content = await getSource({ - url: svgUrlToCopy, - optimize, - }); + const title = svgInfo.title.split(" ").join(""); + const svgUrlToCopy = getSvgUrl(); + let content = await getSource({ + url: svgUrlToCopy, + optimize, + }); - if (svgUrlToCopy) { - content = prefixSvgIds(content, getPrefixFromSvgUrl(svgUrlToCopy)); - } + if (svgUrlToCopy) { + content = prefixSvgIds(content, getPrefixFromSvgUrl(svgUrlToCopy)); + } - if (!content) { - toast.error("Failed to fetch the SVG content", { - duration: 5000, - }); - isLoading = false; - return; - } + if (!content) { + toast.error("Failed to fetch the SVG content", { + duration: 5000, + }); + isLoading = false; + return; + } - const webComponentCode = getWebComponent({ - name: title, - content: content, - }); + const webComponentCode = getWebComponent({ + name: title, + content: content, + }); - await clipboard(webComponentCode); + await clipboard(webComponentCode); - toast.success(`Copied as Web Component`, { - description: `${svgInfo.title} - ${svgInfo.category}`, - }); + toast.success(`Copied as Web Component`, { + description: `${svgInfo.title} - ${svgInfo.category}`, + }); - isLoading = false; + isLoading = false; + } catch (err) { + console.error("Error converting to Web Component:", err); + toast.error("Failed to convert Web Component"); + isLoading = false; + } }; // Copy SVG as Astro component: const convertSvgAstroComponent = async () => { - isLoading = true; - optionsOpen = false; + try { + isLoading = true; + optionsOpen = false; - const svgUrlToCopy = getSvgUrl(); - let content = await getSource({ - url: svgUrlToCopy, - optimize, - }); + const svgUrlToCopy = getSvgUrl(); + let content = await getSource({ + url: svgUrlToCopy, + optimize, + }); - if (svgUrlToCopy) { - content = prefixSvgIds(content, getPrefixFromSvgUrl(svgUrlToCopy)); - } + if (svgUrlToCopy) { + content = prefixSvgIds(content, getPrefixFromSvgUrl(svgUrlToCopy)); + } - if (!content) { - toast.error("Failed to fetch the SVG content", { - duration: 5000, - }); - isLoading = false; - return; - } + if (!content) { + toast.error("Failed to fetch the SVG content", { + duration: 5000, + }); + isLoading = false; + return; + } - const astroComponentCode = getAstroCode({ - svgContent: content, - }); + const astroComponentCode = getAstroCode({ + svgContent: content, + }); - await clipboard(astroComponentCode); + await clipboard(astroComponentCode); - toast.success(`Copied as Astro Component`, { - description: `${svgInfo.title} - ${svgInfo.category}`, - }); + toast.success(`Copied as Astro Component`, { + description: `${svgInfo.title} - ${svgInfo.category}`, + }); - isLoading = false; + isLoading = false; + } catch (err) { + console.error("Error converting to Astro Component:", err); + toast.error("Failed to convert Astro Component"); + isLoading = false; + } }; diff --git a/src/components/svgs/downloadSvg.svelte b/src/components/svgs/downloadSvg.svelte index 75d39d382..d19c5aff0 100644 --- a/src/components/svgs/downloadSvg.svelte +++ b/src/components/svgs/downloadSvg.svelte @@ -123,7 +123,7 @@ {#if typeof svgInfo.route === "string"}
{svgInfo.title} @@ -213,7 +213,7 @@ {#if typeof svgInfo.wordmark === "string" && svgInfo.wordmark !== undefined}
{svgInfo.title} diff --git a/src/routes/api/svgs/svgr/+server.ts b/src/routes/api/svgs/svgr/+server.ts index 27439238e..ac59d509c 100644 --- a/src/routes/api/svgs/svgr/+server.ts +++ b/src/routes/api/svgs/svgr/+server.ts @@ -12,6 +12,10 @@ export const POST = async ({ request }: RequestEvent) => { try { const body = await request.json(); + if (!body.code || !body.name) { + return json({ error: "Missing required fields: code and name" }, { status: 400 }); + } + let svgCode = body.code; const typescript = body.typescript; const name = body.name.replace(/[^a-zA-Z0-9]/g, ""); diff --git a/src/templates/getSource.ts b/src/templates/getSource.ts index 7c250cf71..f53244401 100644 --- a/src/templates/getSource.ts +++ b/src/templates/getSource.ts @@ -6,7 +6,11 @@ interface SourceParams { } export const getSource = async (params: SourceParams) => { - const response = await fetch(params.url || ""); + if (!params.url) return ""; + const response = await fetch(params.url); + if (!response.ok) { + throw new Error(`Failed to fetch SVG: ${response.status} ${response.statusText}`); + } const content = await response.text(); if (!params.optimize) return content; const optimizedContent = optimizeSvg({ svgCode: content }); From 518b8846b014434c7373b272dd549dcdbc546d1c Mon Sep 17 00:00:00 2001 From: piuclaw Date: Thu, 9 Jul 2026 13:32:38 -0300 Subject: [PATCH 2/2] style: apply prettier formatting to lint-related files Run format on files changed in the download/copy bugfix PR. --- src/components/svgs/downloadSvg.svelte | 12 ++---------- src/routes/api/svgs/svgr/+server.ts | 5 ++++- src/templates/getSource.ts | 4 +++- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/components/svgs/downloadSvg.svelte b/src/components/svgs/downloadSvg.svelte index d19c5aff0..e87835bfe 100644 --- a/src/components/svgs/downloadSvg.svelte +++ b/src/components/svgs/downloadSvg.svelte @@ -122,11 +122,7 @@
{#if typeof svgInfo.route === "string"}
- {svgInfo.title} + {svgInfo.title}