From 11fecce73d192a6a040ed56649f63094891560dd Mon Sep 17 00:00:00 2001 From: Crokily Date: Wed, 17 Sep 2025 09:17:14 +1000 Subject: [PATCH] =?UTF-8?q?Revert=20"feat:=20=E6=B7=BB=E5=8A=A0=E5=B7=A5?= =?UTF-8?q?=E4=BD=9C=E6=A8=A1=E5=9D=97=EF=BC=8C=E5=B9=B6=E4=B8=94=E7=94=A8?= =?UTF-8?q?route=E8=B7=AF=E7=94=B1=E8=AF=BB=E5=8F=96=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E7=BB=93=E6=9E=84=EF=BC=8C=E5=8F=91=E9=80=81=E5=88=B0=E5=89=8D?= =?UTF-8?q?=E7=AB=AF=E8=87=AA=E5=8A=A8=E7=94=9F=E6=88=90Tree=20Selector?= =?UTF-8?q?=E7=BB=93=E6=9E=84=EF=BC=8C=E7=94=A8=E4=BA=8E=E4=B8=80=E9=94=AE?= =?UTF-8?q?Contribute"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/docs-tree/route.ts | 138 -- app/components/Community.tsx | 4 +- app/components/Contribute.module.css | 59 - app/components/Contribute.tsx | 291 ---- app/components/Features.tsx | 4 +- app/components/Header.tsx | 2 +- app/components/Hero.tsx | 10 +- app/components/ThemeToggle.tsx | 2 +- {components => app/components}/ui/badge.tsx | 0 {components => app/components}/ui/button.tsx | 0 {components => app/components}/ui/card.tsx | 0 .../HelloWorld.md" | 11 - .../HelloWorld.md" | 11 - .../HelloWorld.md" | 11 - app/layout.tsx | 4 - components/ui/command.tsx | 184 --- components/ui/dialog.tsx | 143 -- components/ui/input.tsx | 21 - next.config.mjs | 1 + package.json | 6 +- pnpm-lock.yaml | 1335 ++--------------- 21 files changed, 104 insertions(+), 2133 deletions(-) delete mode 100644 app/api/docs-tree/route.ts delete mode 100644 app/components/Contribute.module.css delete mode 100644 app/components/Contribute.tsx rename {components => app/components}/ui/badge.tsx (100%) rename {components => app/components}/ui/button.tsx (100%) rename {components => app/components}/ui/card.tsx (100%) delete mode 100644 "app/docs/jobs/\344\270\232\345\212\241\346\265\201/HelloWorld.md" delete mode 100644 "app/docs/jobs/\346\212\200\346\234\257\346\240\210/HelloWorld.md" delete mode 100644 "app/docs/jobs/\347\254\224\350\257\225\351\235\242\347\273\217/HelloWorld.md" delete mode 100644 components/ui/command.tsx delete mode 100644 components/ui/dialog.tsx delete mode 100644 components/ui/input.tsx diff --git a/app/api/docs-tree/route.ts b/app/api/docs-tree/route.ts deleted file mode 100644 index a0e9c1d..0000000 --- a/app/api/docs-tree/route.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { NextResponse } from "next/server"; -import * as fs from "node:fs"; -import * as path from "node:path"; - -export const runtime = "nodejs"; -export const dynamic = "force-dynamic"; -export const revalidate = 0; - -type DirNode = { - name: string; - path: string; // relative to docs root - children?: DirNode[]; -}; - -type Diag = { - cwd: string; - node: string; - hasFs: boolean; - candidates: string[]; - envHints: { - NEXT_RUNTIME: string | null; - NODE_ENV: string | null; - }; -}; - -function hasFs() { - try { - return ( - typeof fs.readdirSync === "function" && - typeof fs.existsSync === "function" - ); - } catch { - return false; - } -} - -function safeListDir(dir: string): { entries: fs.Dirent[]; error?: string } { - try { - return { entries: fs.readdirSync(dir, { withFileTypes: true }) }; - } catch (e) { - return { entries: [], error: String(e) }; - } -} - -function buildTree(root: string, maxDepth = 2, rel = ""): DirNode[] { - const { entries, error } = safeListDir(root); - if (error) throw new Error(`readdir failed at ${root}: ${error}`); - - const dirs = entries.filter((d) => d.isDirectory()); - const nodes: DirNode[] = []; - - for (const e of dirs) { - if (e.name.startsWith(".") || e.name.startsWith("[")) continue; - const abs = path.join(root, e.name); - const nodeRel = rel ? `${rel}/${e.name}` : e.name; - const node: DirNode = { name: e.name, path: nodeRel }; - if (maxDepth > 1) node.children = buildTree(abs, maxDepth - 1, nodeRel); - nodes.push(node); - } - - try { - nodes.sort((a, b) => a.name.localeCompare(b.name, "zh-Hans")); - } catch { - nodes.sort((a, b) => a.name.localeCompare(b.name)); - } - return nodes; -} - -export async function GET() { - const cwd = process.cwd(); - const candidates = [ - path.join(cwd, "app", "docs"), - path.join(cwd, "src", "app", "docs"), - ]; - - const diag: Diag = { - cwd, - node: process.version, - hasFs: hasFs(), - candidates, - envHints: { - NEXT_RUNTIME: process.env.NEXT_RUNTIME ?? null, - NODE_ENV: process.env.NODE_ENV ?? null, - }, - }; - - try { - if (!diag.hasFs) { - return NextResponse.json( - { ok: false, reason: "fs-unavailable", diag }, - { status: 500 }, - ); - } - - // pick the first existing candidate - const docsRoot = candidates.find((p) => fs.existsSync(p)); - if (!docsRoot) { - return NextResponse.json( - { - ok: false, - reason: "docs-root-not-found", - diag: { - ...diag, - exists: Object.fromEntries( - candidates.map((p) => [p, fs.existsSync(p)]), - ), - }, - }, - { status: 500 }, - ); - } - - // try to list - let tree: DirNode[] = []; - try { - tree = buildTree(docsRoot, 2); - } catch (e: unknown) { - const msg = e instanceof Error ? e.message : String(e); - return NextResponse.json( - { - ok: false, - reason: "buildTree-failed", - error: msg, - diag: { ...diag, docsRoot }, - }, - { status: 500 }, - ); - } - - return NextResponse.json({ ok: true, docsRoot, tree, diag }); - } catch (err: unknown) { - const msg = err instanceof Error ? err.message : String(err); - return NextResponse.json( - { ok: false, reason: "unhandled", error: msg, diag }, - { status: 500 }, - ); - } -} diff --git a/app/components/Community.tsx b/app/components/Community.tsx index cac6887..e90a073 100644 --- a/app/components/Community.tsx +++ b/app/components/Community.tsx @@ -1,5 +1,5 @@ -import { Button } from "../../components/ui/button"; -import { Card, CardContent } from "../../components/ui/card"; +import { Button } from "./ui/button"; +import { Card, CardContent } from "./ui/card"; import { ExternalLink, MessageCircle, diff --git a/app/components/Contribute.module.css b/app/components/Contribute.module.css deleted file mode 100644 index 15402ea..0000000 --- a/app/components/Contribute.module.css +++ /dev/null @@ -1,59 +0,0 @@ -@keyframes drift { - 0% { - transform: translateX(-10%); - } - 100% { - transform: translateX(110%); - } -} - -@keyframes twinkle { - 0% { - opacity: 0.35; - transform: scale(1); - } - 50% { - opacity: 1; - transform: scale(1.15); - } - 100% { - opacity: 0.35; - transform: scale(1); - } -} - -.driftSlow { - animation: drift 42s linear infinite; -} - -.driftFast { - animation: drift 26s linear infinite; -} - -.twinkle { - animation: twinkle 2.8s ease-in-out infinite; -} - -.twinkleDelay1 { - animation: twinkle 3.2s ease-in-out infinite; - animation-delay: 0.4s; -} - -.twinkleDelay2 { - animation: twinkle 3.6s ease-in-out infinite; - animation-delay: 0.8s; -} - -.twinkleDelay3 { - animation: twinkle 4s ease-in-out infinite; - animation-delay: 1.2s; -} - -.textGlow { - /* 多层阴影增强对比度:深色描边 + 柔和外晕 */ - text-shadow: - 0 1px 2px rgba(0, 0, 0, 0.55), - 0 2px 6px rgba(0, 0, 0, 0.35), - 0 0 12px rgba(255, 255, 255, 0.12); - -webkit-text-stroke: 0.35px rgba(0, 0, 0, 0.35); -} diff --git a/app/components/Contribute.tsx b/app/components/Contribute.tsx deleted file mode 100644 index 02254b4..0000000 --- a/app/components/Contribute.tsx +++ /dev/null @@ -1,291 +0,0 @@ -"use client"; - -import React, { useEffect, useMemo, useState } from "react"; -import { Button } from "@/components/ui/button"; -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, - DialogTrigger, -} from "@/components/ui/dialog"; -import { Input } from "@/components/ui/input"; -import { ExternalLink, Plus, Sparkles } from "lucide-react"; -import styles from "./Contribute.module.css"; - -// --- antd -import { TreeSelect } from "antd"; -import type { DefaultOptionType } from "antd/es/select"; -import { DataNode } from "antd/es/tree"; - -const REPO_OWNER = "InvolutionHell"; -const REPO_NAME = "involutionhell.github.io"; -const DEFAULT_BRANCH = "main"; -const DOCS_BASE = "app/docs"; - -type DirNode = { name: string; path: string; children?: DirNode[] }; - -function buildGithubNewUrl(dirPath: string, filename: string, title: string) { - const file = filename.endsWith(".mdx") ? filename : `${filename}.mdx`; - const fullDir = `${DOCS_BASE}/${dirPath}`.replace(/\/+/g, "/"); - const frontMatter = `--- -title: ${title || "New Article"} -description: -date: ${new Date().toISOString().slice(0, 10)} -tags: [] ---- - -# ${title || "New Article"} - -Write your content here. -`; - const params = new URLSearchParams({ filename: file, value: frontMatter }); - return `https://github.com/${REPO_OWNER}/${REPO_NAME}/new/${DEFAULT_BRANCH}/${encodeURIComponent(fullDir)}?${params.toString()}`; -} - -// ✅ 用纯文本 label + 一级节点 selectable:false -function toTreeSelectData(tree: DirNode[]): DefaultOptionType[] { - return tree.map((l1) => ({ - key: l1.path, - value: l1.path, - label: l1.name, - selectable: false, // ✅ 一级不可选 - children: [ - ...(l1.children || []).map((l2) => ({ - key: l2.path, - value: l2.path, - label: `${l1.name} / ${l2.name}`, // 纯文本,方便搜索 - isLeaf: true, - })), - { - key: `${l1.path}/__create__`, - value: `${l1.path}/__create__`, - label: ( - - - 在「{l1.name}」下新建二级子栏目… - - ), - }, - ], - })); -} - -export function Contribute() { - const [open, setOpen] = useState(false); - const [tree, setTree] = useState([]); - const [loading, setLoading] = useState(false); - - // ✅ Hooks 必须在组件内部 - const [expandedKeys, setExpandedKeys] = useState([]); // 受控展开状态 - const [selectedKey, setSelectedKey] = useState(""); - const [newSub, setNewSub] = useState(""); - const [articleTitle, setArticleTitle] = useState(""); - const [articleFile, setArticleFile] = useState(""); - - useEffect(() => { - let mounted = true; - (async () => { - setLoading(true); - try { - const res = await fetch("/api/docs-tree", { cache: "no-store" }); - const data = await res.json(); - if (mounted && data?.ok) setTree(data.tree || []); - } catch { - const res = await fetch("/docs-tree.json").catch(() => null); - const data = await res?.json(); - if (mounted && data?.ok) setTree(data.tree || []); - } finally { - setLoading(false); - } - })(); - return () => { - mounted = false; - }; - }, []); - - const options = useMemo(() => toTreeSelectData(tree), [tree]); - - const finalDirPath = useMemo(() => { - if (!selectedKey) return ""; - if (selectedKey.endsWith("/__create__")) { - const l1 = selectedKey.split("/")[0]; - if (!newSub.trim()) return ""; - return `${l1}/${newSub.trim().replace(/\s+/g, "-")}`; - } - return selectedKey; - }, [selectedKey, newSub]); - - const canProceed = !!finalDirPath && (articleTitle || articleFile); - - const handleOpenGithub = () => { - if (!canProceed) return; - const filename = (articleFile || articleTitle || "new-article") - .trim() - .replace(/\s+/g, "-") - .toLowerCase(); - const title = articleTitle || filename; - window.open( - buildGithubNewUrl(finalDirPath, filename, title), - "_blank", - "noopener,noreferrer", - ); - }; - - return ( - { - setOpen(v); - if (!v) { - setSelectedKey(""); - setNewSub(""); - } - }} - > - - - - - - 我要投稿 - - 选择栏目(单选、可搜索;一级仅用于展开),或在一级下新建二级子栏目,然后跳转到 - GitHub 新建文章。 - - - -
- - setSelectedKey((val as string) ?? "")} - showSearch - // 用 label 做过滤(label 都是可读文本) - treeNodeFilterProp="label" - filterTreeNode={(input, node) => - String(node.label ?? "") - .toLowerCase() - .includes(input.toLowerCase()) - } - // ✅ 默认折叠;点标题即可展开/收起 - treeExpandedKeys={expandedKeys} - onTreeExpand={(keys) => setExpandedKeys(keys as string[])} - treeExpandAction="click" - // 下拉不顶满,挂到触发元素父节点内,避免被 Dialog 裁剪 - popupMatchSelectWidth={false} - listHeight={360} - getPopupContainer={(trigger) => - trigger?.parentElement ?? document.body - } - placeholder="请选择(可搜索)" - allowClear - treeLine - /> -
- - {selectedKey.endsWith("/__create__") && ( -
- - setNewSub(e.target.value)} - /> -

- 将创建路径:{selectedKey.split("/")[0]} / {newSub || "<未填写>"} -

-
- )} - -
- - setArticleTitle(e.target.value)} - /> - - setArticleFile(e.target.value)} - /> -
- - -
- 路径预览: - {finalDirPath || "(未选择)"} -
- -
-
-
- ); -} diff --git a/app/components/Features.tsx b/app/components/Features.tsx index 6357339..450f139 100644 --- a/app/components/Features.tsx +++ b/app/components/Features.tsx @@ -1,5 +1,5 @@ -import { Card, CardContent } from "../../components/ui/card"; -import { Badge } from "../../components/ui/badge"; +import { Card, CardContent } from "./ui/card"; +import { Badge } from "./ui/badge"; import { Users, Zap, Heart } from "lucide-react"; import { Github as GithubIcon } from "./icons/Github"; diff --git a/app/components/Header.tsx b/app/components/Header.tsx index e751934..effdfbd 100644 --- a/app/components/Header.tsx +++ b/app/components/Header.tsx @@ -1,5 +1,5 @@ import { ThemeToggle } from "./ThemeToggle"; -import { Button } from "../../components/ui/button"; +import { Button } from "./ui/button"; import { MessageCircle } from "lucide-react"; import { Github as GithubIcon } from "./icons/Github"; diff --git a/app/components/Hero.tsx b/app/components/Hero.tsx index 52fbefe..3453908 100644 --- a/app/components/Hero.tsx +++ b/app/components/Hero.tsx @@ -1,9 +1,8 @@ import Link from "next/link"; -import { Button } from "../../components/ui/button"; +import { Button } from "./ui/button"; import { ExternalLink } from "lucide-react"; import { Github as GithubIcon } from "./icons/Github"; import { ZoteroFeed } from "@/app/components/ZoteroFeed"; -import { Contribute } from "@/app/components/Contribute"; export function Hero() { const categories: { title: string; desc: string; href: string }[] = [ @@ -17,11 +16,6 @@ export function Hero() { desc: "数据结构、算法与基础计算机科学知识", href: "/docs/computer-science", }, - { - title: "笔试面经", - desc: "可以给我一份工作吗?我什么都可以做!", - href: "/docs/jobs", - }, { title: "群友分享", desc: "群友写的捏", @@ -73,8 +67,6 @@ export function Hero() { - {/* 投稿按钮 */} - {/* Top-level directories */}
目录
diff --git a/app/components/ThemeToggle.tsx b/app/components/ThemeToggle.tsx index 1afc9d5..6adce57 100644 --- a/app/components/ThemeToggle.tsx +++ b/app/components/ThemeToggle.tsx @@ -1,6 +1,6 @@ "use client"; import { Moon, Sun } from "lucide-react"; -import { Button } from "../../components/ui/button"; +import { Button } from "./ui/button"; import { useTheme } from "./ThemeProvider"; export function ThemeToggle() { diff --git a/components/ui/badge.tsx b/app/components/ui/badge.tsx similarity index 100% rename from components/ui/badge.tsx rename to app/components/ui/badge.tsx diff --git a/components/ui/button.tsx b/app/components/ui/button.tsx similarity index 100% rename from components/ui/button.tsx rename to app/components/ui/button.tsx diff --git a/components/ui/card.tsx b/app/components/ui/card.tsx similarity index 100% rename from components/ui/card.tsx rename to app/components/ui/card.tsx diff --git "a/app/docs/jobs/\344\270\232\345\212\241\346\265\201/HelloWorld.md" "b/app/docs/jobs/\344\270\232\345\212\241\346\265\201/HelloWorld.md" deleted file mode 100644 index a665e78..0000000 --- "a/app/docs/jobs/\344\270\232\345\212\241\346\265\201/HelloWorld.md" +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: Hello World -description: First page -date: "2025-09-11" -tags: - - intro ---- - -# Hello World - -期待您的投稿 diff --git "a/app/docs/jobs/\346\212\200\346\234\257\346\240\210/HelloWorld.md" "b/app/docs/jobs/\346\212\200\346\234\257\346\240\210/HelloWorld.md" deleted file mode 100644 index a665e78..0000000 --- "a/app/docs/jobs/\346\212\200\346\234\257\346\240\210/HelloWorld.md" +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: Hello World -description: First page -date: "2025-09-11" -tags: - - intro ---- - -# Hello World - -期待您的投稿 diff --git "a/app/docs/jobs/\347\254\224\350\257\225\351\235\242\347\273\217/HelloWorld.md" "b/app/docs/jobs/\347\254\224\350\257\225\351\235\242\347\273\217/HelloWorld.md" deleted file mode 100644 index a665e78..0000000 --- "a/app/docs/jobs/\347\254\224\350\257\225\351\235\242\347\273\217/HelloWorld.md" +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: Hello World -description: First page -date: "2025-09-11" -tags: - - intro ---- - -# Hello World - -期待您的投稿 diff --git a/app/layout.tsx b/app/layout.tsx index c609f04..ff5479e 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -4,7 +4,6 @@ import { RootProvider } from "fumadocs-ui/provider"; import Script from "next/script"; import "./globals.css"; import { ThemeProvider } from "@/app/components/ThemeProvider"; -import { SpeedInsights } from "@vercel/speed-insights/next"; const geistSans = localFont({ src: "./fonts/GeistVF.woff", @@ -52,7 +51,6 @@ export default function RootLayout({
{children}
- {/* 谷歌分析 */}