From 8bf9819173a4437013b31fb901a7369db94b2914 Mon Sep 17 00:00:00 2001 From: Crokily Date: Fri, 19 Sep 2025 16:12:35 +1000 Subject: [PATCH 1/2] feat(contribute): require validated article filename --- app/components/Contribute.tsx | 41 +++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/app/components/Contribute.tsx b/app/components/Contribute.tsx index c863f76..f34e0b2 100644 --- a/app/components/Contribute.tsx +++ b/app/components/Contribute.tsx @@ -23,6 +23,8 @@ import { buildDocsNewUrl } from "@/lib/github"; type DirNode = { name: string; path: string; children?: DirNode[] }; +const FILENAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]*$/; + // 统一调用工具函数生成 GitHub 新建链接,路径规则与 Edit 按钮一致 function buildGithubNewUrl(dirPath: string, filename: string, title: string) { const file = filename.endsWith(".mdx") ? filename : `${filename}.mdx`; @@ -81,6 +83,24 @@ export function Contribute() { const [newSub, setNewSub] = useState(""); const [articleTitle, setArticleTitle] = useState(""); const [articleFile, setArticleFile] = useState(""); + const [articleFileTouched, setArticleFileTouched] = useState(false); + + const trimmedArticleFile = useMemo(() => articleFile.trim(), [articleFile]); + const { isFileNameValid, fileNameError } = useMemo(() => { + if (!trimmedArticleFile) { + return { + isFileNameValid: false, + fileNameError: "请填写文件名。", + }; + } + if (!FILENAME_PATTERN.test(trimmedArticleFile)) { + return { + isFileNameValid: false, + fileNameError: "文件名仅支持英文、数字、连字符或下划线。", + }; + } + return { isFileNameValid: true, fileNameError: "" }; + }, [trimmedArticleFile]); useEffect(() => { let mounted = true; @@ -115,14 +135,11 @@ export function Contribute() { return selectedKey; }, [selectedKey, newSub]); - const canProceed = !!finalDirPath && (articleTitle || articleFile); + const canProceed = !!finalDirPath && isFileNameValid; const handleOpenGithub = () => { if (!canProceed) return; - const filename = (articleFile || articleTitle || "new-article") - .trim() - .replace(/\s+/g, "-") - .toLowerCase(); + const filename = trimmedArticleFile.toLowerCase(); const title = articleTitle || filename; window.open( buildGithubNewUrl(finalDirPath, filename, title), @@ -139,6 +156,9 @@ export function Contribute() { if (!v) { setSelectedKey(""); setNewSub(""); + setArticleTitle(""); + setArticleFile(""); + setArticleFileTouched(false); } }} > @@ -278,12 +298,19 @@ export function Contribute() { value={articleTitle} onChange={(e) => setArticleTitle(e.target.value)} /> - + setArticleFile(e.target.value)} + onChange={(e) => { + setArticleFile(e.target.value); + if (!articleFileTouched) setArticleFileTouched(true); + }} + onBlur={() => setArticleFileTouched(true)} /> + {articleFileTouched && fileNameError && ( +

{fileNameError}

+ )} From d269a645deb95be67ffc5e99f6596c302412303e Mon Sep 17 00:00:00 2001 From: Crokily Date: Fri, 19 Sep 2025 16:25:19 +1000 Subject: [PATCH 2/2] Update app/components/Contribute.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- app/components/Contribute.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/Contribute.tsx b/app/components/Contribute.tsx index f34e0b2..55bb5f5 100644 --- a/app/components/Contribute.tsx +++ b/app/components/Contribute.tsx @@ -23,7 +23,7 @@ import { buildDocsNewUrl } from "@/lib/github"; type DirNode = { name: string; path: string; children?: DirNode[] }; -const FILENAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]*$/; +const FILENAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]+$/; // 统一调用工具函数生成 GitHub 新建链接,路径规则与 Edit 按钮一致 function buildGithubNewUrl(dirPath: string, filename: string, title: string) {