From 4c8615f01c1e5f591829872b09d5e02845f49062 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Sat, 21 Mar 2026 17:45:36 -0500 Subject: [PATCH] refac --- src/lib/components/ImportModal.svelte | 4 ++-- .../admin/Functions/FunctionEditor.svelte | 3 ++- .../workspace/Tools/ToolkitEditor.svelte | 3 ++- src/lib/utils/index.ts | 16 ++++++++++++++++ 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/lib/components/ImportModal.svelte b/src/lib/components/ImportModal.svelte index 2163ce26c..043c03679 100644 --- a/src/lib/components/ImportModal.svelte +++ b/src/lib/components/ImportModal.svelte @@ -6,7 +6,7 @@ import Spinner from '$lib/components/common/Spinner.svelte'; import Modal from '$lib/components/common/Modal.svelte'; import XMark from '$lib/components/icons/XMark.svelte'; - import { extractFrontmatter } from '$lib/utils'; + import { extractFrontmatter, nameToId } from '$lib/utils'; export let show = false; @@ -42,7 +42,7 @@ toast.success(successMessage); let func = res; - func.id = func.id || func.name.replace(/\s+/g, '_').toLowerCase(); + func.id = func.id || nameToId(func.name); const frontmatter = extractFrontmatter(res.content); // Ensure frontmatter is extracted diff --git a/src/lib/components/admin/Functions/FunctionEditor.svelte b/src/lib/components/admin/Functions/FunctionEditor.svelte index 1ef7bddc1..a6f70dc3b 100644 --- a/src/lib/components/admin/Functions/FunctionEditor.svelte +++ b/src/lib/components/admin/Functions/FunctionEditor.svelte @@ -4,6 +4,7 @@ const i18n = getContext('i18n'); + import { nameToId } from '$lib/utils'; import CodeEditor from '$lib/components/common/CodeEditor.svelte'; import ConfirmDialog from '$lib/components/common/ConfirmDialog.svelte'; import Badge from '$lib/components/common/Badge.svelte'; @@ -36,7 +37,7 @@ }; $: if (name && !edit && !clone) { - id = name.replace(/\s+/g, '_').toLowerCase(); + id = nameToId(name); } let codeEditor; diff --git a/src/lib/components/workspace/Tools/ToolkitEditor.svelte b/src/lib/components/workspace/Tools/ToolkitEditor.svelte index 90e8e436a..bc8ad91dc 100644 --- a/src/lib/components/workspace/Tools/ToolkitEditor.svelte +++ b/src/lib/components/workspace/Tools/ToolkitEditor.svelte @@ -8,6 +8,7 @@ import { user } from '$lib/stores'; import { updateToolAccessGrants } from '$lib/apis/tools'; + import { nameToId } from '$lib/utils'; import CodeEditor from '$lib/components/common/CodeEditor.svelte'; import ConfirmDialog from '$lib/components/common/ConfirmDialog.svelte'; import ChevronLeft from '$lib/components/icons/ChevronLeft.svelte'; @@ -45,7 +46,7 @@ }; $: if (name && !edit && !clone) { - id = name.replace(/\s+/g, '_').toLowerCase(); + id = nameToId(name); } let codeEditor; diff --git a/src/lib/utils/index.ts b/src/lib/utils/index.ts index 48ee42a6c..8f9d68eb0 100644 --- a/src/lib/utils/index.ts +++ b/src/lib/utils/index.ts @@ -1411,6 +1411,22 @@ export const slugify = (str: string): string => { ); }; +/** + * Convert a display name into a safe, underscore-delimited identifier. + * Strips emojis, accents, and any non-alphanumeric characters so the + * result is always accepted by backend validation. + * + * e.g. "My Tool 😄" → "my_tool" + */ +export const nameToId = (name: string): string => { + return name + .normalize('NFD') + .replace(/[\u0300-\u036f]/g, '') + .replace(/[^\w]+/g, '_') + .replace(/^_+|_+$/g, '') + .toLowerCase(); +}; + export const extractInputVariables = (text: string): Record => { const regex = /{{\s*([^|}\s]+)\s*\|\s*([^}]+)\s*}}/g; const regularRegex = /{{\s*([^|}\s]+)\s*}}/g;