From 87d9bab5dc7f7415ce5316c7766cdf345b50d833 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A0=81=E5=86=9C=E9=98=BF=E6=A5=A0?= Date: Wed, 9 Sep 2026 10:35:24 +0800 Subject: [PATCH] =?UTF-8?q?feat(web):=20=E6=95=B4=E4=BD=93=E6=9B=BF?= =?UTF-8?q?=E6=8D=A2=20UI=20=E6=A1=86=E6=9E=B6=E4=B8=BA=20Ant=20Design=206?= =?UTF-8?q?=20=E5=B9=B6=E6=8E=A5=E5=85=A5=E8=87=AA=E5=AE=9A=E4=B9=89?= =?UTF-8?q?=E4=B8=BB=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 以 antd ConfigProvider + antd-style 承载 zinc 风格自定义主题(明暗双套 token), button/input/select/notification 用 createStyles 定制;三态主题经 darkAlgorithm 无刷新切换;use-toast 改基于 antd notification; 移除 shadcn/ui 组件目录与 radix 依赖;样式桥接层保证排版与多端布局不变。 --- README.md | 4 +- docs/design.md | 12 +- web/package-lock.json | 1442 ++++++++++++++++++++++++++++++- web/package.json | 3 + web/src/App.tsx | 69 +- web/src/components/Layout.tsx | 198 +++-- web/src/components/Toast.tsx | 30 - web/src/hooks/use-toast.ts | 25 + web/src/index.css | 1194 +++++-------------------- web/src/lib/utils.ts | 4 - web/src/pages/AdminEdit.tsx | 136 +-- web/src/pages/AdminList.tsx | 321 +++---- web/src/pages/AdminSettings.tsx | 142 ++- web/src/pages/AdminTrash.tsx | 131 +-- web/src/pages/Home.tsx | 60 +- web/src/pages/Login.tsx | 106 ++- web/src/pages/NotFound.tsx | 9 +- web/src/pages/Note.tsx | 50 +- web/src/pages/TagPage.tsx | 25 +- web/src/theme/shadcnTheme.ts | 414 +++++++++ web/tsconfig.json | 6 +- web/vite.config.ts | 7 + web/vitest.config.ts | 7 + 23 files changed, 2787 insertions(+), 1608 deletions(-) delete mode 100644 web/src/components/Toast.tsx create mode 100644 web/src/hooks/use-toast.ts create mode 100644 web/src/theme/shadcnTheme.ts diff --git a/README.md b/README.md index bbff514..d7b1a08 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,9 @@ internal/httpapi/ 路由与 handler(公共 / 认证 / 管理 / feed) internal/webui/ go:embed 前端产物 + SPA fallback + index.html meta 注入 web/ React 19 + Vite + Tailwind 4 前端 ├─ src/pages/ 9 页面:首页 / 笔记详情 / 标签页 / 登录 / 后台列表 / 编辑 / 回收站 / 设置 / 404 -├─ src/components/ Layout(导航与主题切换)、Editor、MarkdownViewer、Toast +├─ src/components/ Layout(导航与主题切换)、Editor、MarkdownViewer +├─ src/theme/ shadcnTheme(antd ConfigProvider 主题:品牌 token + 明暗算法 + 桥接样式) +├─ src/hooks/ use-toast(基于 antd notification 的操作反馈) └─ src/lib/ api(CSRF 注入)/ auth(会话上下文)/ theme(三态主题)/ sanitize / invalidate deploy/ systemd 单元 ×3 + Caddyfile ``` diff --git a/docs/design.md b/docs/design.md index d31c1ca..529481e 100644 --- a/docs/design.md +++ b/docs/design.md @@ -100,13 +100,13 @@ | 框架 | React | 19.2.8 | 生态最大、md 渲染/编辑器组件最丰富 | — | | 语言 | TypeScript | 7.x(不动则 5.9) | 原生编译器版已可用;若第三方库兼容有问题回退 5.9 | — | | 构建 | Vite | 8.2.2 | 事实标准;部署在域名根,`base` 用默认值 `'/'`(相对 base 与 SPA 深链冲突,见 §8.3 与审评报告 P0-1) | — | -| 样式/组件 | Tailwind CSS 4 + **shadcn/ui** | 4.3.3 / CLI 4.21.0 | CSS-first 无样板配置;shadcn 复制式组件零运行时、克制美观,正合内容型博客;按需叠加 Radix 原语 | Ant Design 6(重、后台味浓)、Chakra 3、HeroUI、Radix Themes | +| 样式/组件 | Tailwind CSS 4 + **Ant Design 6** | 4.3.3 / 6.6.3 | Tailwind 负责页面排版与设计 token;交互组件统一用 antd(Button/Input/Table/Modal/Dropdown/Tooltip/notification 等),经 ConfigProvider 自定义主题对齐黑白灰品牌 token 并随三态主题切换明暗算法;v6 原生支持 React 19 | shadcn/ui + Radix(曾采用,组件需自维护,替换为 antd 降低长期维护成本)、Chakra 3、HeroUI | | 路由 | react-router | 8.3.1 | library mode 成熟,博客+后台两个区足够 | TanStack Router 1.x(类型安全更强,但本项目路由简单) | | 数据请求 | TanStack Query | 5.102.8 | 十几 kB 换来统一 loading/缓存/重试,值得 | 裸 fetch + 自写 hook | | Markdown 渲染 | react-markdown + remark-gfm + rehype-sanitize + **rehype-highlight** | 10.1.0 / 4.0.1 / 6.0.0 / 7.0.2 | 组合标准;高亮选 rehype-highlight(**纯 class 输出、零内联样式**)而非 shiki(inline style),与严格 CSP 兼容 | shiki 4.4.3(准确度最高但产 inline style)、prism-react-renderer(近停更,不推荐) | | Markdown 编辑器 | **CodeMirror 6**(@uiw/react-codemirror + @codemirror/lang-markdown) | 4.25.11 / 6.5.2 | 源码编辑 + 分屏实时预览,零魔法、可预期;图片粘贴自写 handler(≤50 行) | Tiptap 3.31(体验派,WYSIWYG+成熟图片上传,文档全);MDXEditor(重);ByteMD(字节已弃坑)、Vditor 4(体积大、心智旧) | | 状态管理 | React Query + Context(**不引入** zustand) | — | 会话态 + 服务端缓存已覆盖;确需跨页 UI 态再加 zustand(≈1kB) | redux(不必要) | -| 图标 | lucide-react | 1.42.0 | 活跃、风格统一、与 shadcn 默认匹配 | — | +| 图标 | lucide-react | 1.42.0 | 活跃、风格统一(antd 组件内亦复用) | — | ### 3.3 关键决策记录(ADR 摘要) @@ -194,9 +194,11 @@ pure-note/ │ └── src/ │ ├── main.tsx / App.tsx / router.tsx │ ├── lib/ # api client、auth、csrf、格式化 -│ ├── components/ # shadcn/ui 生成目录 + 业务组件 +│ ├── theme/ # antd ConfigProvider 主题(品牌 token + 明暗算法) +│ ├── components/ # 业务组件 │ │ ├── markdown/ # MarkdownViewer(渲染管线,可替换) │ │ └── editor/ # Editor(CodeMirror)+ ImagePaste + Toolbar +│ ├── hooks/ # use-toast(antd notification 封装) │ ├── features/ # notes / admin 业务 hooks │ └── pages/ # Home / Note / Tags / AdminLogin / AdminList / AdminTrash / AdminEdit └── deploy/ @@ -705,7 +707,7 @@ timer 触发 `pn-maint.service`:先 `pn gc --commit`,后 `pn backup /backup/ | react / react-dom | 19.2.8 | UI 框架 | | vite / @vitejs/plugin-react | 8.2.2 | 构建(base 默认 `'/'`) | | tailwindcss + @tailwindcss/vite + @tailwindcss/typography | 4.3.3 | 样式体系 | -| shadcn/ui(CLI)+ Radix primitives | 4.21.0 | 组件库 | +| antd + antd-style | 6.6.3 / 4.1.0 | 组件库(ConfigProvider 自定义主题 + createStyles 桥接) | | react-router | 8.3.1 | 路由 | | @tanstack/react-query | 5.102.8 | 服务端状态 | | react-markdown + remark-gfm + rehype-sanitize + rehype-highlight | 10.1.0 / 4.0.1 / 6.0.0 / 7.0.2 | Markdown 渲染与防 XSS | @@ -719,7 +721,7 @@ timer 触发 `pn-maint.service`:先 `pn gc --commit`,后 `pn backup /backup/ - https://go.dev/dl/ · https://pkg.go.dev/modernc.org/sqlite - https://github.com/yuin/goldmark · https://github.com/microcosm-cc/bluemonday - https://github.com/facebook/react · https://github.com/vitejs/vite -- https://github.com/shadcn-ui/ui · https://github.com/remix-run/react-router +- https://github.com/ant-design/ant-design · https://github.com/remix-run/react-router - https://github.com/remarkjs/react-markdown · https://github.com/rehypejs/rehype-sanitize - https://github.com/ueberdosis/tiptap · https://github.com/uiwjs/react-codemirror diff --git a/web/package-lock.json b/web/package-lock.json index 07872e8..e84a22f 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -11,6 +11,9 @@ "@codemirror/lang-markdown": "^6.5.2", "@tanstack/react-query": "^5.102.8", "@uiw/react-codemirror": "^4.25.11", + "antd": "^6.6.3", + "antd-style": "^4.1.0", + "clsx": "^2.1.1", "lucide-react": "^1.42.0", "react": "^19.2.8", "react-dom": "^19.2.8", @@ -36,11 +39,103 @@ "vitest": "^3.2.4" } }, + "node_modules/@ant-design/colors": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-8.0.1.tgz", + "integrity": "sha512-foPVl0+SWIslGUtD/xBr1p9U4AKzPhNYEseXYRRo5QSzGACYZrQbe11AYJbYfAWnWSpGBx6JjBmSeugUsD9vqQ==", + "license": "MIT", + "dependencies": { + "@ant-design/fast-color": "^3.0.0" + } + }, + "node_modules/@ant-design/cssinjs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@ant-design/cssinjs/-/cssinjs-2.1.2.tgz", + "integrity": "sha512-2Hy8BnCEH31xPeSLbhhB2ctCPXE2ZnASdi+KbSeS79BNbUhL9hAEe20SkUk+BR8aKTmqb6+FKFruk7w8z0VoRQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.1", + "@emotion/hash": "^0.8.0", + "@emotion/unitless": "^0.7.5", + "@rc-component/util": "^1.4.0", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "stylis": "^4.3.4" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@ant-design/cssinjs-utils": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@ant-design/cssinjs-utils/-/cssinjs-utils-2.1.2.tgz", + "integrity": "sha512-5fTHQ158jJJ5dC/ECeyIdZUzKxE/mpEMRZxthyG1sw/AKRHKgJBg00Yi6ACVXgycdje7KahRNvNET/uBccwCnA==", + "license": "MIT", + "dependencies": { + "@ant-design/cssinjs": "^2.1.2", + "@babel/runtime": "^7.23.2", + "@rc-component/util": "^1.4.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/@ant-design/fast-color": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@ant-design/fast-color/-/fast-color-3.0.1.tgz", + "integrity": "sha512-esKJegpW4nckh0o6kV3Tkb7NPIZYbPnnFxmQDUmL08ukXZAvV85TZBr70eGuke/CIArLaP6aw8lt9KILjnWuOw==", + "license": "MIT", + "engines": { + "node": ">=8.x" + } + }, + "node_modules/@ant-design/icons": { + "version": "6.3.4", + "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-6.3.4.tgz", + "integrity": "sha512-kDoUlppczVyCUTFthF6cHmPzMBYqNZQhu9j6EyIX/YIdM9j9CZNZoJUjzVw7/1v1VO8WwVMmPp15r/uwIAS0AQ==", + "license": "MIT", + "dependencies": { + "@ant-design/colors": "^8.0.1", + "@ant-design/icons-svg": "^4.6.0", + "@rc-component/util": "^1.11.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@ant-design/icons-svg": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.6.0.tgz", + "integrity": "sha512-PRomU725ABMf/lnQp5HiB7my1kjEbFY0D10N4lXYxK6TIB1gKjIVD5MRThpDaezLgw1D774J8eOeeDB0M6wHrQ==", + "license": "MIT" + }, + "node_modules/@ant-design/react-slick": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@ant-design/react-slick/-/react-slick-2.0.0.tgz", + "integrity": "sha512-HMS9sRoEmZey8LsE/Yo6+klhlzU12PisjrVcydW3So7RdklyEd2qehyU6a7Yp+OYN72mgsYs3NFCyP2lCPFVqg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "clsx": "^2.1.1", + "json2mq": "^0.2.0", + "throttle-debounce": "^5.0.0" + }, + "peerDependencies": { + "react": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", @@ -96,7 +191,6 @@ "version": "7.29.8", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.29.8", @@ -130,7 +224,6 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -140,7 +233,6 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", - "dev": true, "license": "MIT", "dependencies": { "@babel/traverse": "^7.29.7", @@ -182,7 +274,6 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -192,7 +283,6 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -226,7 +316,6 @@ "version": "7.29.8", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.29.8" @@ -283,7 +372,6 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.29.7", @@ -298,7 +386,6 @@ "version": "7.29.8", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz", "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.29.7", @@ -317,7 +404,6 @@ "version": "7.29.8", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.29.7", @@ -480,6 +566,181 @@ "w3c-keyname": "^2.2.4" } }, + "node_modules/@emotion/babel-plugin": { + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", + "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.3.3", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" + }, + "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT" + }, + "node_modules/@emotion/babel-plugin/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT" + }, + "node_modules/@emotion/cache": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/cache/node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT" + }, + "node_modules/@emotion/css": { + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/css/-/css-11.13.5.tgz", + "integrity": "sha512-wQdD0Xhkn3Qy2VNcIzbLP9MR8TafI0MJb7BEAXKp+w4+XqErksWR4OXomuDzPsN4InLdGhVe6EYcn2ZIUCpB8w==", + "license": "MIT", + "dependencies": { + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.13.5", + "@emotion/serialize": "^1.3.3", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2" + } + }, + "node_modules/@emotion/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==", + "license": "MIT" + }, + "node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, + "node_modules/@emotion/react": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", + "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "license": "MIT", + "dependencies": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.2", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/serialize/node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" + }, + "node_modules/@emotion/serialize/node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "license": "MIT" + }, + "node_modules/@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "license": "MIT" + }, + "node_modules/@emotion/unitless": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==", + "license": "MIT" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", + "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", + "license": "MIT" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", + "license": "MIT" + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.28.2", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", @@ -926,7 +1187,6 @@ "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", @@ -948,7 +1208,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -958,14 +1217,12 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", - "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -1075,6 +1332,733 @@ "url": "https://github.com/sponsors/oxc-project" } }, + "node_modules/@rc-component/async-validator": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@rc-component/async-validator/-/async-validator-6.0.0.tgz", + "integrity": "sha512-D3AGQwdyE58gmvx6waVSXJ80JGO+IY5L2O8HDnSOex7JNlzB3GuN/4hyHNTdhy2qtOhkpbIjmeAN3tL993wKbA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.4" + }, + "engines": { + "node": ">=14.x" + } + }, + "node_modules/@rc-component/cascader": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@rc-component/cascader/-/cascader-1.22.0.tgz", + "integrity": "sha512-SffrA57aS9oub3VuI7ajPhJTPtaNxngSvtRhD40Rd8dwJ5vfWPSrVanWgeepdWFGBt7EHftIK5RUU0u3rCTwWw==", + "license": "MIT", + "dependencies": { + "@rc-component/select": "~1.10.0", + "@rc-component/tree": "~1.4.0", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/checkbox": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@rc-component/checkbox/-/checkbox-2.0.0.tgz", + "integrity": "sha512-3CXGPpAR9gsPKeO2N78HAPOzU30UdemD6HGJoWVJOpa6WleaGB5kzZj3v6bdTZab31YuWgY/RxV3VKPctn0DwQ==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/collapse": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rc-component/collapse/-/collapse-1.2.0.tgz", + "integrity": "sha512-ZRYSKSS39qsFx93p26bde7JUZJshsUBEQRlRXPuJYlAiNX0vyYlF5TsAm8JZN3LcF8XvKikdzPbgAtXSbkLUkw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/motion": "^1.1.4", + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/color-picker": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@rc-component/color-picker/-/color-picker-3.1.1.tgz", + "integrity": "sha512-OHaCHLHszCegdXmIq2ZRIZBN/EtpT6Wm8SG/gpzLATHbVKc/avvuKi+zlOuk05FTWvgaMmpxAko44uRJ3M+2pg==", + "license": "MIT", + "dependencies": { + "@ant-design/fast-color": "^3.0.1", + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/context": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@rc-component/context/-/context-2.0.2.tgz", + "integrity": "sha512-uiGpAlblCNlziHPwj4S4Iy/oemeuz/hR03mbiEjTCXwsqOIN3BOzsRMyDwpyO5Fm0vIEEJRUf9ZtbRLbhksuTA==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.11.0" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/dialog": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@rc-component/dialog/-/dialog-1.10.0.tgz", + "integrity": "sha512-eDukNlz9vNszAGv7i3zKXdxEd3wgVmNxuJijYt8zvTh17QwTu8KK/bdURRd/lU4qaMzhO1HKKmMrwOnkaw0BvQ==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.3.3", + "@rc-component/portal": "^2.1.0", + "@rc-component/util": "^1.9.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/drawer": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@rc-component/drawer/-/drawer-1.4.2.tgz", + "integrity": "sha512-1ib+fZEp6FBu+YvcIktm+nCQ+Q+qIpwpoaJH6opGr4ofh2QMq+qdr5DLC4oCf5qf3pcWX9lUWPYX652k4ini8Q==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.1.4", + "@rc-component/portal": "^2.1.3", + "@rc-component/util": "^1.9.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/dropdown": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rc-component/dropdown/-/dropdown-1.0.3.tgz", + "integrity": "sha512-YTST/N6kpqpDz3IMuM/PSSZnrDpSOA6dgHv12gPA90ZTSLv2CoqkZ0+9NtwTY6BeO7dstPblSic2QJg7dSFy/g==", + "license": "MIT", + "dependencies": { + "@rc-component/trigger": "^3.0.0", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.11.0", + "react-dom": ">=16.11.0" + } + }, + "node_modules/@rc-component/form": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@rc-component/form/-/form-1.8.6.tgz", + "integrity": "sha512-1EXVsSKPZC6kGrIqxVck7kAWM6T65b6et/n5wIcyiaIa41VrDshD0nVLHoBDEIZr2ATQsOP6icb4XQkNFMLBAw==", + "license": "MIT", + "dependencies": { + "@rc-component/async-validator": "^6.0.0", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/image": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@rc-component/image/-/image-1.10.0.tgz", + "integrity": "sha512-BjeZCRQ+hw+4WAhvrw8rJvy5fckA2xpf/X2XQEOABUHvLTNB9inB98X3Mp54jYQ7g10DfWERQWHXeC4ylxp1Uw==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.0.0", + "@rc-component/portal": "^2.1.2", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/input": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@rc-component/input/-/input-1.3.1.tgz", + "integrity": "sha512-iFvTUT9W+JC/MSin2aGAk8NqsVlTzcExNC9DZariON1IWirju9NoNeEk47an4Q8iHazkoVI/y1LnDi88+CPcig==", + "license": "MIT", + "dependencies": { + "@rc-component/resize-observer": "^1.1.1", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@rc-component/input-number": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@rc-component/input-number/-/input-number-1.6.2.tgz", + "integrity": "sha512-Gjcq7meZlCOiWN1t1xCC+7/s85humHVokTBI7PJgTfoyw5OWF74y3e6P8PHX104g9+b54jsodFIzyaj6p8LI9w==", + "license": "MIT", + "dependencies": { + "@rc-component/mini-decimal": "^1.0.1", + "@rc-component/util": "^1.4.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/listy": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rc-component/listy/-/listy-1.2.3.tgz", + "integrity": "sha512-IXiMjV5s0rczLBlfh7G5nB4M3365mrEeedjwKtf5I+Ns3PqRUsebR2h5u8CeFarsVfLUPC2I5p0h09TNoOWyvQ==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.1.4", + "@rc-component/portal": "^2.0.0", + "@rc-component/resize-observer": "^1.0.0", + "@rc-component/util": "^1.3.1", + "@rc-component/virtual-list": "^1.4.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/mentions": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@rc-component/mentions/-/mentions-1.12.0.tgz", + "integrity": "sha512-v5MBx8zDcqCT+uybJgKpcx3Fgrvj0m3u+A/15RlSBaMx12lLoJZTTYggC3pbYYfohcKujWWUyBz6O6Gp2vGiNA==", + "license": "MIT", + "dependencies": { + "@rc-component/input": "~1.3.1", + "@rc-component/menu": "~1.5.0", + "@rc-component/trigger": "^3.0.0", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/menu": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@rc-component/menu/-/menu-1.5.0.tgz", + "integrity": "sha512-RjvzPsZkwVEg3xu+/jDDqDeUCBJAnWylONFlGeQgg5enlNAnlpybqhaMGE/UPEIvr6hL90PGhjFBguJqj0Qb2Q==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.1.4", + "@rc-component/overflow": "^1.0.0", + "@rc-component/trigger": "^3.0.0", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/mini-decimal": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rc-component/mini-decimal/-/mini-decimal-1.1.4.tgz", + "integrity": "sha512-xiuXcaCwyOWpD8a8scdExFl+bntNphAW8XeenL1ig2en0AAZY0Pcp4pC0dI22qJ+NvxKn9RoNIoRdqYU3BLH4w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.0" + }, + "engines": { + "node": ">=8.x" + } + }, + "node_modules/@rc-component/motion": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@rc-component/motion/-/motion-1.3.3.tgz", + "integrity": "sha512-Xh3IszxvlSv3/PLYFyC2UZi9LNB83yOnkB/LNmRzaypZLvkhqUIPS7MQpGZcCMWrNsXV2p6YTSWbSGvFpEle9A==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.11.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/mutate-observer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@rc-component/mutate-observer/-/mutate-observer-2.0.1.tgz", + "integrity": "sha512-AyarjoLU5YlxuValRi+w8JRH2Z84TBbFO2RoGWz9d8bSu0FqT8DtugH3xC3BV7mUwlmROFauyWuXFuq4IFbH+w==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.2.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/notification": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@rc-component/notification/-/notification-2.0.8.tgz", + "integrity": "sha512-MTRA3b8eHE14gh5R9nlXzbWZEDEBFVkEHj/FI1VOXw2zrSnSxqbuuU3drsq+k53e6MHwtegoFay1tnNYhtl61Q==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.1.4", + "@rc-component/util": "^1.11.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/overflow": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rc-component/overflow/-/overflow-1.0.1.tgz", + "integrity": "sha512-syfmgAABaHCnCDzPwHZ/2tuvIcpOO3jefYZMmfkN+pmo8HKTzsfhS57vxo4ksPdN0By+uWVJhJWNFozNBxi2eA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.1", + "@rc-component/resize-observer": "^1.0.1", + "@rc-component/util": "^1.4.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/pagination": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@rc-component/pagination/-/pagination-1.4.0.tgz", + "integrity": "sha512-CW1g7P9V8u+e8JQdUsl2RWg+GCsoee0mtJjZUCCxn/vb3jzOwDKm6hAdwddHCVBfWJ58eGUBZz3IvnU8rRktjw==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/picker": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@rc-component/picker/-/picker-1.12.2.tgz", + "integrity": "sha512-ZB1A+tzTQkgtR4flPrAMvxIAOelIIOOOsp22fU2Nad6PbZcJacnyGJUJqUH7ZS9mwxeUwurf1pLMKXrVsK1tlA==", + "license": "MIT", + "dependencies": { + "@rc-component/overflow": "^1.0.0", + "@rc-component/resize-observer": "^1.0.0", + "@rc-component/trigger": "^3.6.15", + "@rc-component/util": "^1.13.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=12.x" + }, + "peerDependencies": { + "date-fns": ">= 2.x", + "dayjs": ">= 1.x", + "luxon": ">= 3.x", + "moment": ">= 2.x", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + }, + "peerDependenciesMeta": { + "date-fns": { + "optional": true + }, + "dayjs": { + "optional": true + }, + "luxon": { + "optional": true + }, + "moment": { + "optional": true + } + } + }, + "node_modules/@rc-component/portal": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@rc-component/portal/-/portal-2.2.1.tgz", + "integrity": "sha512-ck+r1kW/JSv0wxPji3KN2ss9K6Z0qqwusw/mf/0JobXhZ8hC2ejZwCJObW/SvDi0uhA0VzmCnx0CaCci95tcmA==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.11.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=12.x" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/progress": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rc-component/progress/-/progress-1.0.3.tgz", + "integrity": "sha512-Q1nPLIvKr95el/q76yaBh5nv1hqCsWaHQb8U7vXEDMefEhHgM7n3CPoN+o4LHnTyANQHmMFgoS/naAT4Bhxuaw==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/qrcode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@rc-component/qrcode/-/qrcode-2.0.0.tgz", + "integrity": "sha512-aAv3QhPP1xyafuTZOxub6a54pCeBnN3IwQkpETrBtthq4BL5IgxnCbuoBWPDpdLw1y1j6BgBUCAKV92+yX06Dw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.7" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/rate": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rc-component/rate/-/rate-1.0.1.tgz", + "integrity": "sha512-bkXxeBqDpl5IOC7yL7GcSYjQx9G8H+6kLYQnNZWeBYq2OYIv1MONd6mqKTjnnJYpV0cQIU2z3atdW0j1kttpTw==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/resize-observer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@rc-component/resize-observer/-/resize-observer-1.1.2.tgz", + "integrity": "sha512-t/Bb0W8uvL4PYKAB3YcChC+DlHh0Wt5kM7q/J+0qpVEUMLe7Hk5zuvc9km0hMnTFPSx5Z7Wu/fzCLN6erVLE8Q==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.2.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/segmented": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@rc-component/segmented/-/segmented-1.3.0.tgz", + "integrity": "sha512-5J/bJ01mbDnoA6P/FW8SxUvKn+OgUSTZJPzCNnTBntG50tzoP7DydGhqxp7ggZXZls7me3mc2EQDXakU3iTVFg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.1", + "@rc-component/motion": "^1.1.4", + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@rc-component/select": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@rc-component/select/-/select-1.10.1.tgz", + "integrity": "sha512-H+yQsl+qED9NilQ3g6zdpsMwUgwVjrcMTkNHAWRVU/MoNCYgTbDgU+MIMgZDK+rVdd2JUfI/MkysMcZZ0cyQKw==", + "license": "MIT", + "dependencies": { + "@rc-component/overflow": "^1.0.0", + "@rc-component/trigger": "^3.0.0", + "@rc-component/util": "^1.11.1", + "@rc-component/virtual-list": "^1.2.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/@rc-component/slider": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@rc-component/slider/-/slider-1.1.1.tgz", + "integrity": "sha512-LSzgWGYDgeCDgR4r1XlU29gbYws6HpLnvJd/uMhLeW/vQgxldeR+Wb4uzHDCHiYEbr1bnEHWdjkPxjJRHxuiig==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/steps": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rc-component/steps/-/steps-1.2.3.tgz", + "integrity": "sha512-/b9gHcvDGjTDWJZW7+1kKFvzs0jrEeQ8mFvvIgX+h7xNT0FsOYODJYKjyJR4fPbvMBCfCOEqaBK66HBxov5BQA==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/switch": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rc-component/switch/-/switch-1.0.3.tgz", + "integrity": "sha512-Jgi+EbOBquje/XNdofr7xbJQZPYJP+BlPfR0h+WN4zFkdtB2EWqEfvkXJWeipflwjWip0/17rNbxEAqs8hVHfw==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/table": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@rc-component/table/-/table-1.11.1.tgz", + "integrity": "sha512-OWdS6DMmeWb7bJBGqPxYZpQbzBlBiXZUu2sqo6Ii7Sjs9GeK1IsrXrWk26SL2c6KEseabswdxrRj7WUm9LdECw==", + "license": "MIT", + "dependencies": { + "@rc-component/context": "^2.0.1", + "@rc-component/resize-observer": "^1.0.0", + "@rc-component/util": "^1.11.1", + "@rc-component/virtual-list": "^1.0.1", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/tabs": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@rc-component/tabs/-/tabs-1.13.0.tgz", + "integrity": "sha512-3FXr+9ZII8tFLWuDWrgjRaxZd2mazkKJAGRFEfVmp4r1qnBglOlxQ78l54Td1aj6/iFaaEReKSAF2HToztU5uw==", + "license": "MIT", + "dependencies": { + "@rc-component/dropdown": "~1.0.0", + "@rc-component/menu": "~1.5.0", + "@rc-component/motion": "^1.1.3", + "@rc-component/resize-observer": "^1.0.0", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/tooltip": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@rc-component/tooltip/-/tooltip-1.5.2.tgz", + "integrity": "sha512-VLq4BclU3LgRe46g2hTr2bdsM8tFYs7dVwpuD88rPNNg4lCGe8mBSS4FiRzYzS+ffnZ46y4SI8KYKo9K7qBnhw==", + "license": "MIT", + "dependencies": { + "@rc-component/trigger": "^3.10.0", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/tour": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@rc-component/tour/-/tour-2.4.0.tgz", + "integrity": "sha512-aui4r4TqmTzwaBgcQxHYep8kM8PTjZFufjokObpy35KfFeZ0k9ArquWFZqegQlH24P14t+F0qO0mGTgzlav1yg==", + "license": "MIT", + "dependencies": { + "@rc-component/portal": "^2.2.0", + "@rc-component/trigger": "^3.0.0", + "@rc-component/util": "^1.7.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/tree": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@rc-component/tree/-/tree-1.4.0.tgz", + "integrity": "sha512-dGsJGDJQedA0BqqVgj3F8BvHXTSZijyhTXdbAdkcx8lynzZkty/CV3Z3LOm/fxz+BCfl3dfGiAQpb7Q5XNvl0Q==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.0.0", + "@rc-component/util": "^1.11.1", + "@rc-component/virtual-list": "^1.2.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=10.x" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/@rc-component/tree-select": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/@rc-component/tree-select/-/tree-select-1.16.1.tgz", + "integrity": "sha512-a1Oi6EJhqAhdOxxupdJi6fP0RPHMKn5TcfkX2+llaQ4lF4nwfH7b6SCHcnsybaa2s+pk1yZYwVyeOYkDnEBRdg==", + "license": "MIT", + "dependencies": { + "@rc-component/select": "~1.10.0", + "@rc-component/tree": "~1.4.0", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/@rc-component/trigger": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@rc-component/trigger/-/trigger-3.10.1.tgz", + "integrity": "sha512-mXlDN0IXdtV8Yqqm8195ECCyrbmfvvfKvwVvSlH0+qvKD6BUF8gRhEjSy0FOcD1+CcDRHgTiX99LoxfQrmh3Cw==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.3.3", + "@rc-component/portal": "^2.2.1", + "@rc-component/resize-observer": "^1.1.2", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/upload": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@rc-component/upload/-/upload-1.1.1.tgz", + "integrity": "sha512-GvYWSKeaJTOxxC5p6+nOSadzfvXA1h8C/iHFPFZX+szH3JUXrvs+DLiW8YUTBgvMh8m63mJeHrlYlJzAlg+pDA==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/util": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@rc-component/util/-/util-1.13.0.tgz", + "integrity": "sha512-IXf2zBfZrbQGT+G8LxnoICDBGnMHIPIiq5HCRn3EDfKUx28x1rnbZBj46ULJf4sM8ImTG1SepqfNBdxZziqQ4w==", + "license": "MIT", + "dependencies": { + "is-mobile": "^5.0.0", + "react-is": "^19.2.7" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/util/node_modules/react-is": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.8.tgz", + "integrity": "sha512-s5un28nYxKJw5gvUHyW5PCC28CvBqLu9r3cWgzHT4Vo/5fqqkFcdRYsGcKf50WMPpjjFZS5d76fn3YCo2njKwQ==", + "license": "MIT" + }, + "node_modules/@rc-component/virtual-list": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@rc-component/virtual-list/-/virtual-list-1.5.1.tgz", + "integrity": "sha512-boqHxdtyWC88u8quYgEO49bcBy5fzRiOcnBge+N4nLzs2k8hUQ/yw7JE9dM6yCBE4jSm5YSHVCVMS+suBuJGKA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^8.0.0", + "@rc-component/resize-observer": "^1.0.1", + "@rc-component/util": "^1.4.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/virtual-list/node_modules/@babel/runtime": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-8.0.0.tgz", + "integrity": "sha512-sL6cvO2IfkSu/iU+zs2S/w01B7A8V7suXSIKEN4hPFFdZoiPGxrj5pAG0lCaqLWiEIrjKzdznIWuaLcxPR53qw==", + "license": "MIT" + }, "node_modules/@rolldown/binding-android-arm-eabi": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.7.tgz", @@ -2243,6 +3227,12 @@ "undici-types": "~8.9.0" } }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "license": "MIT" + }, "node_modules/@types/react": { "version": "19.2.18", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz", @@ -2476,6 +3466,90 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/antd": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/antd/-/antd-6.6.3.tgz", + "integrity": "sha512-USnOxcOhWEQc65pHapSiCrZOijWef783XnqYZv8fuYIcYVoFxNxl6TsnsUhmoJyZheD/XwAx9CyO+hlyekDCQA==", + "license": "MIT", + "dependencies": { + "@ant-design/colors": "^8.0.1", + "@ant-design/cssinjs": "^2.1.2", + "@ant-design/cssinjs-utils": "^2.1.2", + "@ant-design/fast-color": "^3.0.1", + "@ant-design/icons": "^6.3.4", + "@ant-design/react-slick": "~2.0.0", + "@babel/runtime": "^7.29.2", + "@rc-component/cascader": "~1.22.0", + "@rc-component/checkbox": "~2.0.0", + "@rc-component/collapse": "~1.2.0", + "@rc-component/color-picker": "~3.1.1", + "@rc-component/dialog": "~1.10.0", + "@rc-component/drawer": "~1.4.2", + "@rc-component/dropdown": "~1.0.3", + "@rc-component/form": "~1.8.6", + "@rc-component/image": "~1.10.0", + "@rc-component/input": "~1.3.1", + "@rc-component/input-number": "~1.6.2", + "@rc-component/listy": "~1.2.3", + "@rc-component/mentions": "~1.12.0", + "@rc-component/menu": "~1.5.0", + "@rc-component/motion": "^1.3.3", + "@rc-component/mutate-observer": "^2.0.1", + "@rc-component/notification": "~2.0.8", + "@rc-component/pagination": "~1.4.0", + "@rc-component/picker": "~1.12.2", + "@rc-component/progress": "~1.0.3", + "@rc-component/qrcode": "~2.0.0", + "@rc-component/rate": "~1.0.1", + "@rc-component/resize-observer": "^1.1.2", + "@rc-component/segmented": "~1.3.0", + "@rc-component/select": "~1.10.1", + "@rc-component/slider": "~1.1.1", + "@rc-component/steps": "~1.2.3", + "@rc-component/switch": "~1.0.3", + "@rc-component/table": "~1.11.1", + "@rc-component/tabs": "~1.13.0", + "@rc-component/tooltip": "~1.5.2", + "@rc-component/tour": "~2.4.0", + "@rc-component/tree": "~1.4.0", + "@rc-component/tree-select": "~1.16.1", + "@rc-component/trigger": "^3.10.1", + "@rc-component/upload": "~1.1.1", + "@rc-component/util": "^1.13.0", + "clsx": "^2.1.1", + "dayjs": "^1.11.11", + "scroll-into-view-if-needed": "^3.1.0", + "throttle-debounce": "^5.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ant-design" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/antd-style": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/antd-style/-/antd-style-4.1.0.tgz", + "integrity": "sha512-vnPBGg0OVlSz90KRYZhxd89aZiOImTiesF+9MQqN8jsLGZUQTjbP04X9jTdEfsztKUuMbBWg/RmB/wHTakbtMQ==", + "license": "MIT", + "dependencies": { + "@ant-design/cssinjs": "^2.0.0", + "@babel/runtime": "^7.24.1", + "@emotion/cache": "^11.11.0", + "@emotion/css": "^11.11.2", + "@emotion/react": "^11.11.4", + "@emotion/serialize": "^1.1.3", + "@emotion/utils": "^1.2.1", + "use-merge-value": "^1.2.0" + }, + "peerDependencies": { + "antd": ">=6.0.0", + "react": ">=18" + } + }, "node_modules/aria-query": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", @@ -2496,6 +3570,21 @@ "node": ">=12" } }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, "node_modules/bail": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", @@ -2576,6 +3665,15 @@ "node": ">=8" } }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001810", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", @@ -2674,6 +3772,15 @@ "node": ">= 16" } }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/codemirror": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz", @@ -2699,6 +3806,12 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/compute-scroll-into-view": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.1.tgz", + "integrity": "sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==", + "license": "MIT" + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -2712,6 +3825,31 @@ "integrity": "sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==", "license": "MIT" }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cosmiconfig/node_modules/yaml": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", + "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, "node_modules/crelt": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.7.tgz", @@ -2737,6 +3875,12 @@ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "license": "MIT" }, + "node_modules/dayjs": { + "version": "1.11.23", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.23.tgz", + "integrity": "sha512-QDTCU0M0MxR3hQfnlDJfwekQiaanm1ubOD231u73WBckQ/fsamwRLiE2GBz6D3a/xF1NgfiDLJjXBa1hYOYTtQ==", + "license": "MIT" + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -2850,6 +3994,24 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-module-lexer": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", @@ -2975,6 +4137,12 @@ } } }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "license": "MIT" + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -2990,6 +4158,15 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -3026,6 +4203,18 @@ "node": ">=20.0.0" } }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/hast-util-is-element": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", @@ -3120,6 +4309,21 @@ "node": ">=12.0.0" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, "node_modules/html-url-attributes": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", @@ -3130,6 +4334,22 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/inline-style-parser": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", @@ -3160,6 +4380,27 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-decimal": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", @@ -3180,6 +4421,12 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/is-mobile": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-mobile/-/is-mobile-5.0.0.tgz", + "integrity": "sha512-Tz/yndySvLAEXh+Uk8liFCxOwVH6YutuR74utvOcu7I9Di+DwM0mtdPVZNaVvvBUM2OXxne/NhOs1zAO7riusQ==", + "license": "MIT" + }, "node_modules/is-plain-obj": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", @@ -3206,14 +4453,12 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, "license": "MIT" }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, "license": "MIT", "bin": { "jsesc": "bin/jsesc" @@ -3222,6 +4467,21 @@ "node": ">=6" } }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, + "node_modules/json2mq": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz", + "integrity": "sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==", + "license": "MIT", + "dependencies": { + "string-convert": "^0.2.0" + } + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -3508,6 +4768,12 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, "node_modules/longest-streak": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", @@ -4466,6 +5732,18 @@ "node": ">=18" } }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/parse-entities": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", @@ -4491,6 +5769,39 @@ "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", "license": "MIT" }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/pathe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", @@ -4512,7 +5823,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, "license": "ISC" }, "node_modules/picomatch": { @@ -4779,6 +6089,36 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/rolldown": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.7.tgz", @@ -4872,6 +6212,15 @@ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", "license": "MIT" }, + "node_modules/scroll-into-view-if-needed": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz", + "integrity": "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==", + "license": "MIT", + "dependencies": { + "compute-scroll-into-view": "^3.0.2" + } + }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -4889,6 +6238,15 @@ "dev": true, "license": "ISC" }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -4923,6 +6281,12 @@ "dev": true, "license": "MIT" }, + "node_modules/string-convert": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz", + "integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==", + "license": "MIT" + }, "node_modules/stringify-entities": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", @@ -4981,6 +6345,24 @@ "inline-style-parser": "0.2.7" } }, + "node_modules/stylis": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.4.0.tgz", + "integrity": "sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==", + "license": "MIT" + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/tailwindcss": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.3.tgz", @@ -5002,6 +6384,15 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/throttle-debounce": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-5.0.2.tgz", + "integrity": "sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==", + "license": "MIT", + "engines": { + "node": ">=12.22" + } + }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", @@ -5083,6 +6474,14 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", @@ -5236,6 +6635,15 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/use-merge-value": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-merge-value/-/use-merge-value-1.2.0.tgz", + "integrity": "sha512-DXgG0kkgJN45TcyoXL49vJnn55LehnrmoHc7MbKi+QDBvr8dsesqws8UlyIWGHMR+JXgxc1nvY+jDGMlycsUcw==", + "license": "MIT", + "peerDependencies": { + "react": ">= 16.x" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/web/package.json b/web/package.json index 8270297..cc7283a 100644 --- a/web/package.json +++ b/web/package.json @@ -14,6 +14,9 @@ "@codemirror/lang-markdown": "^6.5.2", "@tanstack/react-query": "^5.102.8", "@uiw/react-codemirror": "^4.25.11", + "antd": "^6.6.3", + "antd-style": "^4.1.0", + "clsx": "^2.1.1", "lucide-react": "^1.42.0", "react": "^19.2.8", "react-dom": "^19.2.8", diff --git a/web/src/App.tsx b/web/src/App.tsx index 84d0b75..c3741fb 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -1,7 +1,11 @@ -import { lazy, Suspense } from 'react' -import { Navigate, Route, Routes } from 'react-router' +import { lazy, Suspense, useEffect, useState } from 'react' import type { ReactNode } from 'react' +import { Navigate, Route, Routes } from 'react-router' +import { App as AntdApp, ConfigProvider } from 'antd' +import zhCN from 'antd/locale/zh_CN' import { useAuth } from './lib/auth' +import { useTheme } from './lib/theme' +import useShadcnTheme from './theme/shadcnTheme' import { BlogLayout, AdminLayout } from './components/Layout' import Home from './pages/Home' import TagPage from './pages/TagPage' @@ -16,6 +20,27 @@ import NotFound from './pages/NotFound' const Note = lazy(() => import('./pages/Note')) const AdminEdit = lazy(() => import('./pages/AdminEdit')) +// antd ConfigProvider:跟随应用三态主题切换 light/dark 算法(system 监听系统变化) +function AntdProvider({ children }: { children: ReactNode }) { + const { mode } = useTheme() + const [systemDark, setSystemDark] = useState( + () => window.matchMedia('(prefers-color-scheme: dark)').matches, + ) + useEffect(() => { + const mq = window.matchMedia('(prefers-color-scheme: dark)') + const onChange = (e: MediaQueryListEvent) => setSystemDark(e.matches) + mq.addEventListener('change', onChange) + return () => mq.removeEventListener('change', onChange) + }, []) + const dark = mode === 'dark' || (mode === 'system' && systemDark) + const configProps = useShadcnTheme(dark) + return ( + + {children} + + ) +} + // 登录守卫:401 统一跳转 /admin/login(§8.2) function RequireAuth({ children }: { children: ReactNode }) { const { authenticated, loading } = useAuth() @@ -30,24 +55,26 @@ function RequireAuth({ children }: { children: ReactNode }) { export default function App() { return ( - 加载中…}> - - }> - } /> - } /> - } /> - } /> - - } /> - }> - } /> - } /> - } /> - } /> - } /> - - } /> - - + + 加载中…}> + + }> + } /> + } /> + } /> + } /> + + } /> + }> + } /> + } /> + } /> + } /> + } /> + + } /> + + + ) } diff --git a/web/src/components/Layout.tsx b/web/src/components/Layout.tsx index 126c32a..4b8feda 100644 --- a/web/src/components/Layout.tsx +++ b/web/src/components/Layout.tsx @@ -1,63 +1,50 @@ -import { useEffect, useRef, useState } from 'react' import { Link, NavLink, Outlet, useLocation, useNavigate } from 'react-router' import { Moon, Sun, Monitor, ExternalLink, House, PenLine, MoreHorizontal, Tags, Trash2, Settings, LogOut, } from 'lucide-react' +import { Dropdown, Tooltip } from 'antd' +import type { MenuProps } from 'antd' import { useSiteTitle } from '../features/site' import { useTheme } from '../lib/theme' import { useAuth } from '../lib/auth' -/* ── 溢出菜单:触发按钮 + 下拉面板(点击外部 / Esc / 选中项关闭) ── */ +/* ── 溢出菜单:antd Dropdown(外点关闭/Esc/焦点管理由 antd 承接) ── */ -function MoreMenu({ triggerClassName, triggerLabel, children }: { +function MoreMenu({ triggerClassName, triggerLabel, items }: { triggerClassName: string triggerLabel: string - children: React.ReactNode + items: MenuProps['items'] }) { - const [open, setOpen] = useState(false) - const ref = useRef(null) - useEffect(() => { - if (!open) return - const onPointer = (e: PointerEvent) => { - if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false) - } - const onKey = (e: KeyboardEvent) => { - if (e.key === 'Escape') setOpen(false) - } - document.addEventListener('pointerdown', onPointer) - document.addEventListener('keydown', onKey) - return () => { - document.removeEventListener('pointerdown', onPointer) - document.removeEventListener('keydown', onKey) - } - }, [open]) return ( -
- - {open && ( -
setOpen(false)}> - {children} -
- )} + // wrapper:与设计稿一致占一个 flex 槽位(display:none 触发器不产生 gap, + // 曾致桌面导航整体横移 4px) +
+ + +
) } +/* 菜单内路由项:激活态样式见 index.css 的 .pn-dd-item-active 桥接规则 */ +function moreNavItem(to: string, end: boolean, icon: React.ReactNode, label: string, active: boolean): NonNullable[number] { + return { + key: to, + className: active ? 'pn-dd-item-active' : undefined, + label: ( + + {icon} + {label} + + ), + } +} + /* ── 博客端:居中单栏 720px 布局(博客系列设计稿) ─────────── */ -function ThemeButton({ className = 'pn-site-navlink', tip = 'native' }: { - className?: string - tip?: 'native' | 'css' -}) { +function ThemeButton({ className = 'pn-site-navlink' }: { className?: string }) { const { mode, setMode } = useTheme() const next = mode === 'light' ? 'dark' : mode === 'dark' ? 'system' : 'light' const title = `主题:${mode === 'light' ? '浅色' : mode === 'dark' ? '深色' : '跟随系统'}(点击切换)` @@ -65,8 +52,7 @@ function ThemeButton({ className = 'pn-site-navlink', tip = 'native' }: { + + ) +} + export function AdminTopbar({ title, children }: { title: string; children?: React.ReactNode }) { const { logout } = useAuth() const navigate = useNavigate() @@ -146,24 +151,41 @@ export function AdminTopbar({ title, children }: { title: string; children?: Rea

{title}

{children} - + {/* <768px 侧栏隐藏、topbar 空间有限:主题/返回博客/退出登录收进溢出菜单 */} - - - - 返回博客 - - - + setMode(next), + label: ( + <> + {mode === 'light' ? : mode === 'dark' ? : } + 主题:{modeLabel} + + ), + }, + { + key: 'blog', + label: ( + + 返回博客 + + ), + }, + { + key: 'logout', + onClick: () => void logout().then(() => navigate('/')), + label: ( + <> + 退出登录 + + ), + }, + ]} + />
) @@ -220,25 +242,27 @@ function AdminSidebar() { 站点所有者 - - - - + + + + + + + +
diff --git a/web/src/components/Toast.tsx b/web/src/components/Toast.tsx deleted file mode 100644 index 04d581b..0000000 --- a/web/src/components/Toast.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { useEffect } from 'react' -import { CircleCheck, TriangleAlert } from 'lucide-react' - -/** - * Toast:右下角操作反馈(设计稿 toast 契约)。 - * message 为 null 时隐藏;展示 2.6s 后调用 onDone 清除。 - */ -export default function Toast({ - message, - kind = 'success', - onDone, -}: { - message: string | null - kind?: 'success' | 'error' - onDone: () => void -}) { - useEffect(() => { - if (!message) return - const t = setTimeout(onDone, 2600) - return () => clearTimeout(t) - }, [message, onDone]) - - if (!message) return null - return ( -
- {kind === 'error' ? : } - {message} -
- ) -} diff --git a/web/src/hooks/use-toast.ts b/web/src/hooks/use-toast.ts new file mode 100644 index 0000000..3946525 --- /dev/null +++ b/web/src/hooks/use-toast.ts @@ -0,0 +1,25 @@ +import { App } from 'antd' + +/** + * 操作反馈:基于 antd notification(经 ConfigProvider 主题 + App 上下文)。 + * 保留旧 toast 契约——成功/错误两种、2.6s 自动消失、右下角(移动端底部通栏)。 + */ +type ToastInput = { + description: string + variant?: 'success' | 'error' +} + +export function useToast() { + const notification = App.useApp().notification + return { + toast: ({ description, variant }: ToastInput) => { + const notify = variant === 'error' ? notification.error : notification.success + notify({ + message: '', + description, + placement: 'bottomRight', + duration: 2.6, + }) + }, + } +} diff --git a/web/src/index.css b/web/src/index.css index 57d56a1..807bae5 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -159,21 +159,34 @@ body { font-size: 14px; line-height: 1.6; } -button, -input, -textarea, -select { - font: inherit; +/* 表单元素复位必须放在 @layer base:裸规则的无 layer 样式会压过 + utilities 层,导致组件(Button/Input/Tabs…)的字号/颜色 + 工具类全部失效(曾致激活 Tab 黑底黑字) */ +@layer base { + button, + input, + textarea, + select { + font: inherit; + color: inherit; + } + ::placeholder { + color: var(--muted-foreground); + } +} +/* antd v6 会在应用根内给所有 注入 :where()(特异度 0) 的 + color/background 无 layer 重置(--ant-color-link),压过 utilities + 层的链接配色工具类;这里以更高特异度拿回链接默认形态 + (继承文本色、无下划线)。各组件类可按更高特异度覆盖。 */ +a { color: inherit; + text-decoration: none; } a:focus-visible, button:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; } -::placeholder { - color: var(--muted-foreground); -} .pn-loading { min-height: 100vh; display: grid; @@ -183,216 +196,170 @@ button:focus-visible { } /* ============================================================ - 共享组件类 —— 源自设计稿 components.css + antd 桥接 —— 组件样式由 antd(ConfigProvider 主题)承接, + 这里只放两类规则:a) 语义色桥接(antd 组件内链接/占位符沿用 + 应用 token);b) 旧设计稿中必须保留的少量组件级样式覆盖。 + 注意:antd cssinjs 为无 layer 样式,同特异度时会压过 Tailwind + utilities 与本文件,需要覆盖时务必提高选择器特异度。 ============================================================ */ - -/* ── Button ── */ -.btn { +/* antd 组件内的路由链接(Tag 内标签链接等)继承组件前景色 */ +.ant-tag a, +.pn-tag-link { + color: inherit; + text-decoration: none; +} +/* 表单占位符沿用应用的 muted 前景(antd 默认偏浅) */ +input.ant-input::placeholder, +textarea.ant-input::placeholder { + color: var(--muted-foreground); +} +/* antd 按钮的 danger-outline 形态(默认描边 + 危险色文字) */ +button.ant-btn.pn-btn-danger-outline { + color: var(--destructive-text); + border-color: var(--destructive-border); + background: transparent; +} +button.ant-btn.pn-btn-danger-outline:hover:not(:disabled) { + color: var(--destructive-text) !important; + border-color: var(--destructive-border) !important; + background: var(--destructive-subtle) !important; +} +/* Dropdown 内路由项:激活态与旧 .pn-moremenu-item.active 一致 */ +.pn-dd-link { display: inline-flex; align-items: center; - justify-content: center; - gap: var(--space-4); - min-width: 0; - border: 1px solid transparent; - border-radius: var(--radius-md); - padding: var(--space-4) calc(var(--space-4) * 3); - font: 600 14px/1 var(--font-sans); - cursor: pointer; + gap: 8px; + color: inherit; text-decoration: none; - transition: background-color 0.16s ease, border-color 0.16s ease, - color 0.16s ease, box-shadow 0.16s ease, filter 0.16s ease; } -.btn:focus-visible { - outline: 2px solid var(--color-primary, var(--primary)); - outline-offset: 2px; -} -.btn:disabled { - cursor: not-allowed; - opacity: 0.5; - box-shadow: none; -} -.btn.primary { - background: var(--primary); - color: var(--primary-foreground); - box-shadow: var(--shadow-sm); -} -.btn.primary:hover:not(:disabled), -.btn.primary:active:not(:disabled) { - filter: brightness(0.96); -} -.btn.secondary { - background: var(--card); - color: var(--foreground); - border-color: var(--border); -} -.btn.secondary:hover:not(:disabled), -.btn.secondary:active:not(:disabled) { +li.ant-dropdown-menu-item.pn-dd-item-active { background: var(--muted); } -.btn.ghost { - background: transparent; +li.ant-dropdown-menu-item.pn-dd-item-active span { + font-weight: 600; color: var(--foreground); - padding: calc(var(--space-4) * 0.75) calc(var(--space-4) * 2.5); } -.btn.ghost:hover:not(:disabled), -.btn.ghost:active:not(:disabled) { - background: var(--muted); +li.ant-dropdown-menu-item.pn-dd-item-active svg { + color: var(--foreground); } -.btn.danger { - background: var(--destructive); - color: var(--destructive-foreground); +/* 标签云(antd CheckableTag):计数用等宽字体,选中态反白 */ +.pn-cloud-tag { + display: inline-flex; } -.btn.danger:hover:not(:disabled), -.btn.danger:active:not(:disabled) { - filter: brightness(0.94); +.pn-cloud-tag a { + display: inline-flex; + align-items: center; + gap: 6px; + color: inherit; + text-decoration: none; } -.btn svg { +.pn-cloud-count { + font-family: var(--font-mono); + font-size: 12px; + color: var(--muted-foreground); +} +.ant-tag-checkable-checked .pn-cloud-count { + color: rgba(255, 255, 255, 0.85); +} +/* 表单字段标签(antd 无独立 Label 组件,保留原生 label + 应用排版) */ +.pn-field-label { + font-size: 14px; + font-weight: 500; + color: var(--foreground); +} +.pn-ae-pfield .pn-field-label { + font-size: 12px; +} +.pn-input-icon { + color: var(--muted-foreground); +} +/* 设置卡片标题/描述(旧 CardTitle/CardDescription 排版) */ +.pn-as-card-title { + margin: 0; + font-size: 15px; + font-weight: 600; + color: var(--foreground); +} +.pn-as-card-desc { + margin: 0; + font-size: 13px; + line-height: 1.6; + color: var(--muted-foreground); +} +/* 编辑器侧栏标题(旧 CardTitle + mb-3) */ +.pn-ae-side-title { + margin: 0 0 12px; + font-size: 14px; + line-height: 1.6; + font-weight: 600; + color: var(--foreground); +} +/* 登录页(antd 版):锁图标 / 错误条 / 返回博客间距 */ +.pn-login-lock { + color: var(--muted-foreground); +} +.pn-login-alert { + margin-top: 14px; +} +button.pn-login-back { + margin-top: 4px; + color: var(--muted-foreground); + font-weight: 500; +} +button.pn-login-back:hover { + color: var(--foreground); +} +/* 笔记详情:私有预览条(antd Alert info + 行内图标对齐) */ +.pn-note-preview-alert { + margin-top: 32px; +} +.pn-note-preview-alert .ant-alert-message { + display: inline-flex; + align-items: center; + gap: 8px; +} +.pn-note-preview-alert svg { + width: 15px; + height: 15px; flex: none; } - -/* ── Card ── */ -.card { +/* 确认弹层正文(antd Modal 内容排版沿用旧契约) */ +.pn-modal-desc { + margin: 8px 0 0; + font-size: 13px; + line-height: 1.7; + color: var(--muted-foreground); + overflow-wrap: anywhere; +} +.pn-modal-error { + margin: 8px 0 0; + font-size: 12px; + color: var(--destructive-text); +} +/* 指标卡:antd Card body 为块级,恢复旧卡片三行纵向堆叠 */ +.pn-al-metric .ant-card-body { display: flex; flex-direction: column; - gap: 6px; - padding: 16px; - border: 1px solid var(--border); - border-radius: var(--radius-md); - background: var(--card); - box-shadow: var(--shadow-sm); -} -.card .title { - font-size: 14px; - font-weight: 600; - color: var(--card-foreground); -} -.card .meta { - font-size: 12px; - color: var(--muted-foreground); -} -.card .divider { - margin: 12px 0; - border: 0; - border-top: 1px solid var(--border); -} -.card .foot { - display: flex; - align-items: center; - gap: 12px; -} -.eyebrow { - font-size: 12px; - color: var(--muted-foreground); -} -.metric .value { - font-size: 26px; - font-weight: 700; - letter-spacing: -0.03em; - color: var(--card-foreground); -} -.metric { gap: 2px; } - -/* ── Pill ── */ -.pill { - display: inline-flex; - align-items: center; - gap: 6px; - align-self: flex-start; - padding: 3px 8px; - border-radius: var(--radius-md); - background: var(--primary); - color: var(--primary-foreground); - font-size: 11px; - font-weight: 600; - line-height: 1.4; +/* 移动端通知:贴近旧 toast 的底部通栏位(antd wrapper 为行内样式,需 !important) */ +@media (max-width: 767px) { + .ant-notification { + left: 16px !important; + right: 16px !important; + bottom: 80px !important; + } + .pn-al-metric .ant-card-body { + padding: 12px; + } + .pn-as-card .ant-card-body { + padding: 20px; + } } -/* ── Tabs(分段控件) ── */ -.tabs { - display: inline-flex; - align-items: center; - gap: 4px; - padding: 4px; - border: 1px solid var(--border); - border-radius: var(--radius-md); - background: var(--muted); -} -.tabs a, -.tabs button { - display: inline-flex; - align-items: center; - justify-content: center; - min-height: 32px; - padding: 0 12px; - border: 0; - border-radius: calc(var(--radius-md) - 3px); - background: transparent; - color: var(--muted-foreground); - text-decoration: none; - white-space: nowrap; - font-size: 13px; - font-weight: 500; - cursor: pointer; - transition: background-color 0.15s ease, color 0.15s ease; -} -.tabs a:hover, -.tabs button:hover { - color: var(--foreground); -} -.tabs .active { - background: var(--primary); - color: var(--primary-foreground); -} -.tabs .active:hover { - color: var(--primary-foreground); -} -/* ── Table ── */ -.table-card { - min-width: 0; - flex: 1; - background: var(--card); - border: 1px solid var(--border); - border-radius: var(--radius-md); - box-shadow: var(--shadow-sm); - overflow: hidden; -} -.table { - width: 100%; - border-collapse: collapse; -} -.table th, -.table td { - padding: 12px 16px; - border-bottom: 1px solid var(--border); - text-align: left; - vertical-align: middle; -} -.table th { - font-size: 11px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.06em; - color: var(--muted-foreground); - background: var(--muted); - white-space: nowrap; -} -.table td { - color: var(--card-foreground); - font-size: 13px; -} -.table tbody tr:last-child td { - border-bottom: 0; -} -.table tbody tr { - transition: background-color 0.15s ease; -} -.table tbody tr:hover { - background: var(--muted); -} -.table-card .empty { +/* 表格卡空态(无数据/加载中占位) */ +.pn-empty { display: grid; place-items: center; min-height: 132px; @@ -402,7 +369,7 @@ button:focus-visible { font-size: 13px; } -/* ── 状态点 / 分隔符 ── */ +/* ── 状态点 ── */ .dot { width: 7px; height: 7px; @@ -410,10 +377,6 @@ button:focus-visible { background: currentColor; flex: none; } -.sep { - padding: 0 8px 0 0; - color: var(--muted-foreground); -} /* ============================================================ 博客端共享 shell —— 居中单栏 720px(设计稿博客系列) @@ -645,27 +608,6 @@ button:focus-visible { gap: 8px; margin-top: 14px; } -.pn-home-tag { - display: inline-flex; - align-items: center; - padding: 4px 10px; - border-radius: var(--radius-sm); - background: var(--muted); - font-size: 12px; - font-weight: 500; - color: var(--muted-foreground); - text-decoration: none; - transition: background-color 0.15s ease, color 0.15s ease; -} -.pn-home-tag::before { - content: "#"; - margin-right: 3px; - opacity: 0.65; -} -.pn-home-tag:hover { - background: var(--accent); - color: var(--foreground); -} .pn-home-list-empty { padding: 64px 0; text-align: center; @@ -687,45 +629,33 @@ button:focus-visible { color: var(--muted-foreground); white-space: nowrap; } -.pn-pager { - display: flex; - align-items: center; - gap: 4px; - flex-shrink: 0; -} -.pn-pager .btn.ghost { - width: 28px; - height: 28px; - padding: 0; - color: var(--muted-foreground); -} -.pn-pager .btn.ghost:hover:not(:disabled) { - color: var(--foreground); -} -.pn-page-btn { - width: 28px; - height: 28px; +/* 首页标签筛选条:链接型分段导航(配色走无 layer 类规则, + 避免 antd 的 重置压过 utilities 层) */ +.pn-home-filter a { display: inline-flex; align-items: center; justify-content: center; - border: 1px solid transparent; - border-radius: 999px; - background: transparent; - font-size: 12.5px; + min-height: 32px; + padding: 0 12px; + border-radius: calc(var(--radius-md) - 3px); + font-size: 13px; font-weight: 500; + white-space: nowrap; color: var(--muted-foreground); - cursor: pointer; - transition: background-color 0.15s ease, color 0.15s ease; + transition: color 0.15s ease; } -.pn-page-btn:hover { - background: var(--muted); +.pn-home-filter a:hover { color: var(--foreground); } -.pn-page-btn.is-active { +.pn-home-filter a[aria-current='page'] { background: var(--primary); color: var(--primary-foreground); - font-weight: 600; } +.pn-home-filter a:focus-visible { + outline: 2px solid var(--primary); + outline-offset: 2px; +} +/* 首页分页器已由 antd Pagination 承接(.pn-home-pager 仅保留布局行) */ @media (max-width: 640px) { .pn-home-content { padding-top: 40px; @@ -745,14 +675,15 @@ button:focus-visible { padding-top: 40px; padding-bottom: 8px; } -.pn-note-back { +button.pn-note-back { margin-left: -10px; gap: 6px; font-size: 13px; font-weight: 500; + line-height: 1; color: var(--muted-foreground); } -.pn-note-back:hover { +button.pn-note-back:hover { color: var(--foreground); } .pn-note-back svg { @@ -793,40 +724,6 @@ button:focus-visible { width: 14px; height: 14px; } -.pn-note-tag { - padding: 3px 10px; - border-radius: var(--radius-md); - font-size: 12px; - font-weight: 500; - background: transparent; - color: var(--muted-foreground); - border: 1px solid var(--border); - text-decoration: none; - transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease; -} -.pn-note-tag:hover { - color: var(--foreground); - background: var(--muted); - border-color: var(--border-strong); -} -/* 私有预览提示(保留既有功能,套用 token) */ -.pn-note-private { - display: flex; - align-items: center; - gap: 8px; - margin-top: 32px; - padding: 10px 14px; - border: 1px solid var(--border); - border-radius: var(--radius-md); - background: var(--muted); - color: var(--muted-foreground); - font-size: 13px; -} -.pn-note-private svg { - flex: none; - width: 15px; - height: 15px; -} /* 上一篇 / 下一篇 */ .pn-note-postnav { display: grid; @@ -837,11 +734,15 @@ button:focus-visible { border-top: 1px solid var(--border); } .pn-note-nav-card { - padding: 18px 20px; - gap: 8px; text-decoration: none; transition: background-color 0.15s ease, border-color 0.15s ease; } +.pn-note-nav-card .ant-card-body { + display: flex; + flex-direction: column; + gap: 8px; + padding: 18px 20px; +} .pn-note-nav-card:hover { background: var(--muted); border-color: var(--border-strong); @@ -1097,45 +998,6 @@ button:focus-visible { flex-wrap: wrap; gap: 8px; } -.pn-tag-pill { - display: inline-flex; - align-items: center; - gap: 6px; - padding: 8px 14px; - border: 1px solid var(--border); - border-radius: var(--radius-md); - background: var(--card); - color: var(--foreground); - font-size: 14px; - font-weight: 500; - line-height: 1; - text-decoration: none; - transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease; -} -.pn-tag-pill:hover { - background: var(--muted); -} -.pn-tag-pill svg { - width: 14px; - height: 14px; - flex: none; - color: var(--muted-foreground); -} -.pn-tag-count { - font-family: var(--font-mono); - font-size: 12px; - color: var(--muted-foreground); -} -.pn-tag-pill.active { - background: var(--primary); - border-color: var(--primary); - color: var(--primary-foreground); - box-shadow: none; -} -.pn-tag-pill.active svg, -.pn-tag-pill.active .pn-tag-count { - color: var(--primary-foreground); -} .pn-tag-groups { margin-top: 48px; display: flex; @@ -1269,10 +1131,6 @@ button:focus-visible { align-items: center; justify-content: center; } -.pn-404-actions .btn svg { - width: 16px; - height: 16px; -} @media (max-width: 480px) { .pn-404-code { font-size: 56px; @@ -1319,13 +1177,6 @@ button:focus-visible { letter-spacing: var(--tracking-tight); color: var(--foreground); } -.pn-login-card { - box-sizing: border-box; - width: 100%; - max-width: 380px; - padding: 28px 24px; - gap: 0; -} .pn-login-title { margin: 0 0 6px; font-size: 16px; @@ -1339,92 +1190,6 @@ button:focus-visible { line-height: 1.6; color: var(--muted-foreground); } -.pn-login-field { - position: relative; - display: flex; - align-items: center; -} -.pn-login-icon { - position: absolute; - left: 12px; - width: 16px; - height: 16px; - color: var(--muted-foreground); - pointer-events: none; -} -.pn-login-input { - box-sizing: border-box; - width: 100%; - height: 44px; - padding: 0 42px 0 38px; - border: 1px solid var(--input); - border-radius: var(--radius-sm); - background: var(--card); - color: var(--foreground); - font: 14px/1.4 var(--font-sans); - outline: none; - transition: border-color 0.15s ease, box-shadow 0.15s ease; -} -.pn-login-input:hover { - border-color: var(--border-strong); -} -.pn-login-input:focus { - outline: 2px solid var(--ring); - outline-offset: 1px; - border-color: var(--ring); -} -.pn-login-eye { - position: absolute; - right: 6px; - display: inline-flex; - align-items: center; - justify-content: center; - width: 30px; - height: 30px; - padding: 0; - border: 0; - border-radius: var(--radius-sm); - background: transparent; - color: var(--muted-foreground); - cursor: pointer; - transition: background-color 0.15s ease, color 0.15s ease; -} -.pn-login-eye:hover { - background: var(--muted); - color: var(--foreground); -} -.pn-login-eye svg { - width: 16px; - height: 16px; -} -.pn-login-error { - display: flex; - align-items: center; - gap: 8px; - margin-top: 14px; - padding: 10px 12px; - border: 1px solid var(--destructive-border); - border-radius: var(--radius-sm); - background: var(--destructive-subtle); - color: var(--destructive-text); - font-size: 13px; - line-height: 1.5; -} -.pn-login-error svg { - flex: none; - width: 15px; - height: 15px; -} -.pn-login-submit { - width: 100%; - height: 44px; - margin-top: 20px; - font-size: 14px; -} -.pn-login-submit svg { - width: 16px; - height: 16px; -} .pn-login-hint { margin: 16px 0 0; text-align: center; @@ -1432,25 +1197,20 @@ button:focus-visible { line-height: 1.6; color: var(--muted-foreground); } -.pn-login-back { - color: var(--muted-foreground); - font-weight: 500; -} -.pn-login-back:hover { - color: var(--foreground); - background: var(--muted); -} -.pn-login-back svg { - width: 14px; - height: 14px; +/* 登录卡片(antd Card):内边距由本类承接(body padding 已置 0) */ +.pn-login-card { + box-sizing: border-box; + width: 100%; + max-width: 380px; + padding: 28px 24px; } @media (max-width: 480px) { - .pn-login-card { - padding: 24px 20px; - } .pn-login-wordmark { font-size: 26px; } + .pn-login-card { + padding: 24px 20px; + } } /* ============================================================ @@ -1624,141 +1384,14 @@ button:focus-visible { width: 15px; height: 15px; } -/* CSS 悬浮 tooltip(原生 title 延迟高且部分环境不显示) */ -.pn-admin-iconbtn::after { - content: attr(data-tip); - position: absolute; - left: 50%; - bottom: calc(100% + 10px); - z-index: 30; - padding: 4px 8px; - border-radius: var(--radius-sm); - background: var(--primary); - color: var(--primary-foreground); - font-size: 11px; - font-weight: 500; - line-height: 1.4; - white-space: nowrap; - opacity: 0; - pointer-events: none; - transform: translate(-50%, 2px); - transition: opacity 0.15s ease, transform 0.15s ease; -} -.pn-admin-iconbtn::before { - content: ""; - position: absolute; - left: 50%; - bottom: 100%; - z-index: 30; - margin-left: -5px; - border: 5px solid transparent; - border-top-color: var(--primary); - opacity: 0; - pointer-events: none; - transition: opacity 0.15s ease; -} -.pn-admin-iconbtn:hover::after, -.pn-admin-iconbtn:focus-visible::after { - opacity: 1; - transform: translate(-50%, 0); -} -.pn-admin-iconbtn:hover::before, -.pn-admin-iconbtn:focus-visible::before { - opacity: 1; -} -/* tooltip 朝下变体(topbar 贴近视口顶部,气泡须向下弹出)。 - 气泡右对齐按钮:居中锚定会越过 topbar 右缘产生页面横向滚动条 */ -.pn-admin-iconbtn.tip-below::after { - bottom: auto; - top: calc(100% + 10px); - left: auto; - right: 0; - transform: translate(0, -2px); -} -.pn-admin-iconbtn.tip-below::before { - bottom: auto; - top: 100%; - border-top-color: transparent; - border-bottom-color: var(--primary); -} -.pn-admin-iconbtn.tip-below:hover::after, -.pn-admin-iconbtn.tip-below:focus-visible::after { - transform: translate(0, 0); -} -/* 侧栏隐藏时的移动端兜底按钮(<768px 显示) */ +/* 悬浮提示由 antd Tooltip 承接;侧栏隐藏时的移动端兜底按钮(<768px 显示) */ .pn-admin-mobile-act { display: none; } -/* 溢出菜单:面板与菜单项(博客 header / 后台 topbar 共用) */ +/* 溢出菜单挂载点(面板由 antd Dropdown 渲染,wrapper 仅保留占位语义) */ .pn-moremenu { position: relative; } -.pn-moremenu-panel { - position: absolute; - top: calc(100% + 8px); - right: 0; - z-index: 60; - min-width: 150px; - display: flex; - flex-direction: column; - gap: 2px; - padding: 6px; - border: 1px solid var(--border); - border-radius: var(--radius-md); - background: var(--popover); - box-shadow: var(--shadow-sm); - animation: pn-moremenu-in 0.15s ease; -} -@keyframes pn-moremenu-in { - from { - opacity: 0; - transform: translateY(-4px); - } - to { - opacity: 1; - transform: none; - } -} -.pn-moremenu-item { - display: flex; - align-items: center; - gap: 10px; - padding: 8px 10px; - border: 0; - border-radius: var(--radius-sm); - background: transparent; - color: var(--foreground); - font-size: 13px; - font-weight: 500; - line-height: 1.4; - text-align: left; - text-decoration: none; - white-space: nowrap; - cursor: pointer; - transition: background-color 0.15s ease; -} -.pn-moremenu-item:hover, -.pn-moremenu-item.active { - background: var(--muted); - color: var(--foreground); -} -.pn-moremenu-item.active { - font-weight: 600; -} -.pn-moremenu-item svg { - width: 15px; - height: 15px; - flex: none; - color: var(--muted-foreground); -} -.pn-moremenu-item.active svg { - color: var(--foreground); -} -/* 无 data-tip 的图标按钮不渲染空 tooltip 气泡(如菜单触发按钮) */ -.pn-admin-iconbtn:not([data-tip])::after, -.pn-admin-iconbtn:not([data-tip])::before { - display: none; -} .pn-admin-avatar { width: 28px; height: 28px; @@ -1821,20 +1454,6 @@ button:focus-visible { gap: 10px; flex-shrink: 0; } -.pn-admin-actions .btn { - height: 34px; - font-size: 13px; -} -.pn-admin-actions .btn.ghost { - color: var(--muted-foreground); -} -.pn-admin-actions .btn.ghost:hover { - color: var(--foreground); -} -.pn-admin-actions .btn svg { - width: 15px; - height: 15px; -} .pn-admin-content { padding: 24px; display: flex; @@ -1869,6 +1488,7 @@ button:focus-visible { .pn-admin-search input { width: 100%; min-width: 0; + padding: 0; border: 0; outline: 0; background: transparent; @@ -1908,7 +1528,8 @@ button:focus-visible { .pn-admin-actions { gap: 6px; } - .pn-admin-actions .btn { + /* antd 按钮移动端收紧水平内边距(沿用设计稿紧凑规格) */ + .pn-admin-actions .ant-btn { padding-left: 10px; padding-right: 10px; } @@ -1972,24 +1593,6 @@ button:focus-visible { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; } -.pn-al-metrics .card { - padding: 16px 18px; - gap: 2px; - box-shadow: var(--shadow-xs); -} -.pn-al-metrics .eyebrow { - font-size: 11.5px; - letter-spacing: 0.04em; -} -.pn-al-metrics .value { - font-size: 28px; - line-height: 1.2; - margin: 2px 0; -} -.pn-al-metrics .meta { - font-size: 12px; - line-height: 1.5; -} .pn-al-table-head { display: flex; align-items: center; @@ -2009,14 +1612,6 @@ button:focus-visible { color: var(--muted-foreground); white-space: nowrap; } -.pn-table-scroll { - width: 100%; - overflow-x: auto; - overscroll-behavior-x: contain; -} -.pn-al-table-card .table { - min-width: 600px; -} .pn-al-note-title { display: flex; align-items: center; @@ -2052,18 +1647,6 @@ button:focus-visible { flex-wrap: wrap; gap: 6px; } -.pn-al-tag { - display: inline-flex; - align-items: center; - padding: 2px 9px; - border: 1px solid var(--border); - border-radius: 999px; - background: var(--card); - font-size: 11.5px; - line-height: 1.6; - color: var(--muted-foreground); - white-space: nowrap; -} .pn-al-date { font-family: var(--font-mono); font-size: 12.5px; @@ -2102,25 +1685,6 @@ button:focus-visible { white-space: nowrap; text-align: right; } -.pn-icon-btn { - width: 30px; - height: 30px; - padding: 0; - border-radius: var(--radius-sm); - color: var(--muted-foreground); -} -.pn-icon-btn:hover:not(:disabled) { - color: var(--foreground); -} -.pn-icon-btn.danger { - background: transparent; - color: var(--destructive-text); -} -.pn-icon-btn.danger:hover:not(:disabled) { - background: var(--destructive-subtle); - color: var(--destructive-text); - filter: none; -} .pn-al-table-foot { display: flex; align-items: center; @@ -2138,15 +1702,6 @@ button:focus-visible { .pn-al-metrics { gap: 8px; } - .pn-al-metrics .card { - padding: 12px; - } - .pn-al-metrics .value { - font-size: 22px; - } - .pn-al-metrics .meta { - display: none; - } .pn-al-table-note { display: none; } @@ -2165,12 +1720,9 @@ button:focus-visible { gap: 24px; align-items: start; } -.pn-ae-editor { - padding: 24px; - gap: 0; - min-width: 0; -} -.pn-ae-title-input { +/* 编辑卡与发布卡的 Card 基础样式由 ui/card 承接(gap-0/p-6 等) */ +/* 提权至 (0,1,1)+:压过 antd .ant-input 的同名字段(cssinjs 无 layer 且后注入) */ +input.pn-ae-title-input { width: 100%; margin: 0 0 14px; padding: 0; @@ -2183,7 +1735,7 @@ button:focus-visible { color: var(--foreground); outline: none; } -.pn-ae-title-input::placeholder { +input.pn-ae-title-input::placeholder { color: var(--muted-foreground); } .pn-ae-meta { @@ -2220,16 +1772,6 @@ button:focus-visible { gap: 8px; margin-left: auto; } -.pn-ae-tag { - flex-direction: row; - align-self: auto; - padding: 4px 6px 4px 10px; - border: 1px solid var(--border); - background: var(--card); - color: var(--foreground); - font-size: 12px; - font-weight: 500; -} .pn-ae-tag-x { display: inline-flex; align-items: center; @@ -2252,7 +1794,7 @@ button:focus-visible { width: 12px; height: 12px; } -.pn-ae-add-tag { +button.pn-ae-add-tag { height: 26px; gap: 6px; padding: 0 10px; @@ -2261,15 +1803,15 @@ button:focus-visible { font-size: 12px; font-weight: 500; } -.pn-ae-add-tag:hover { +button.pn-ae-add-tag:hover { background: var(--muted); color: var(--foreground); } -.pn-ae-add-tag svg { +button.pn-ae-add-tag svg { width: 13px; height: 13px; } -.pn-ae-tag-input { +input.pn-ae-tag-input { height: 26px; width: 110px; padding: 0 10px; @@ -2280,7 +1822,7 @@ button:focus-visible { font-size: 12px; outline: none; } -.pn-ae-tag-input:focus { +input.pn-ae-tag-input:focus { border-color: var(--ring); } .pn-ae-toolbar { @@ -2292,18 +1834,18 @@ button:focus-visible { border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); } -.pn-ae-tool { +button.pn-ae-tool { width: 32px; height: 32px; padding: 0; border-radius: var(--radius-sm); color: var(--muted-foreground); } -.pn-ae-tool:hover:not(:disabled) { +button.pn-ae-tool:hover:not(:disabled) { background: var(--accent); color: var(--foreground); } -.pn-ae-tool svg { +button.pn-ae-tool svg { width: 15px; height: 15px; } @@ -2340,15 +1882,6 @@ button:focus-visible { flex-direction: column; gap: 16px; } -.pn-ae-publish { - padding: 20px; - gap: 0; -} -.pn-ae-publish .title { - margin: 0 0 12px; - font-size: 14px; - font-weight: 600; -} .pn-ae-prow { display: flex; align-items: center; @@ -2384,43 +1917,6 @@ button:focus-visible { gap: 6px; padding: 10px 0 2px; } -.pn-ae-pfield > label { - font-size: 12px; - font-weight: 500; - color: var(--muted-foreground); -} -.pn-ae-input { - width: 100%; - height: 34px; - padding: 0 10px; - border: 1px solid var(--input); - border-radius: var(--radius-sm); - background: var(--card); - color: var(--foreground); - font-family: var(--font-mono); - font-size: 12px; - outline: none; - box-sizing: border-box; - transition: border-color 0.15s ease; -} -.pn-ae-input:focus { - border-color: var(--ring); - outline: 2px solid var(--ring); - outline-offset: 2px; -} -.pn-ae-input.is-error { - border-color: var(--destructive); - background: var(--destructive-subtle); -} -.pn-ae-textarea { - height: auto; - min-height: 68px; - padding: 8px 10px; - font-family: var(--font-sans); - font-size: 12.5px; - line-height: 1.6; - resize: vertical; -} .pn-ae-phint { font-size: 11px; line-height: 1.5; @@ -2431,58 +1927,9 @@ button:focus-visible { font-size: 12px; color: var(--destructive-text); } -.pn-ae-divider { +.ant-divider.pn-ae-divider { margin: 14px 0; } -.pn-ae-danger { - width: 100%; - height: 34px; - gap: 7px; - border: 1px solid var(--destructive-border); - border-radius: var(--radius-sm); - background: transparent; - color: var(--destructive-text); - font-size: 13px; - font-weight: 500; - padding: 0 12px; -} -.pn-ae-danger:hover:not(:disabled) { - background: var(--destructive-subtle); -} -.pn-ae-danger svg { - width: 15px; - height: 15px; -} -/* 开关(发布设置) */ -.pn-switch { - position: relative; - width: 36px; - height: 20px; - border: 0; - border-radius: 999px; - background: var(--border-strong); - cursor: pointer; - transition: background-color 0.15s ease; - flex: none; -} -.pn-switch[aria-checked="true"] { - background: var(--primary); -} -.pn-switch::after { - content: ""; - position: absolute; - top: 2px; - left: 2px; - width: 16px; - height: 16px; - border-radius: 999px; - background: var(--card); - box-shadow: var(--shadow-xs); - transition: transform 0.15s ease; -} -.pn-switch[aria-checked="true"]::after { - transform: translateX(16px); -} @media (max-width: 900px) { .pn-ae-grid { grid-template-columns: minmax(0, 1fr); @@ -2503,30 +1950,6 @@ button:focus-visible { /* ============================================================ 回收站(回收站.html) ============================================================ */ -.pn-at-alert { - display: flex; - align-items: flex-start; - gap: 10px; - padding: 12px 14px; - background: var(--muted); - border: 1px solid var(--border); - border-radius: var(--radius-md); - color: var(--muted-foreground); - font-size: 13px; - line-height: 1.6; -} -.pn-at-alert svg { - width: 16px; - height: 16px; - flex: none; - margin-top: 2px; -} -.pn-at-alert p { - margin: 0; -} -.pn-at-table-card .table { - min-width: 560px; -} .pn-at-title-cell { min-width: 220px; } @@ -2564,10 +1987,6 @@ button:focus-visible { gap: 4px; white-space: nowrap; } -.pn-at-actions .btn svg { - width: 14px; - height: 14px; -} /* ============================================================ 站点设置(站点设置.html) @@ -2575,25 +1994,11 @@ button:focus-visible { .pn-as-content { max-width: 640px; } -.pn-as-card { - gap: 0; - padding: 24px; -} .pn-as-card-head { display: flex; flex-direction: column; gap: 4px; } -.pn-as-card-head .title { - font-size: 15px; -} -.pn-as-card-head .meta { - font-size: 13px; - line-height: 1.6; -} -.pn-as-card .divider { - margin: 16px 0 20px; -} .pn-as-fields { display: flex; flex-direction: column; @@ -2609,77 +2014,6 @@ button:focus-visible { font-weight: 500; color: var(--muted-foreground); } -.pn-as-input { - width: 100%; - height: 38px; - padding: 0 12px; - border: 1px solid var(--input); - border-radius: var(--radius-sm); - background: var(--card); - color: var(--foreground); - font-family: var(--font-sans); - font-size: 14px; - line-height: 1.5; - outline: none; - box-sizing: border-box; - transition: border-color 0.15s ease, box-shadow 0.15s ease; -} -textarea.pn-as-input { - height: auto; - min-height: 76px; - padding: 8px 12px; - resize: vertical; - line-height: 1.7; -} -.pn-as-input:hover { - border-color: var(--border-strong); -} -.pn-as-input:focus { - border-color: var(--ring); - box-shadow: 0 0 0 2px var(--ring); -} -.pn-as-control { - position: relative; - display: flex; - align-items: center; -} -.pn-as-control .pn-as-input { - padding-left: 36px; -} -.pn-as-control.has-toggle .pn-as-input { - padding-right: 40px; -} -.pn-as-leading-icon { - position: absolute; - left: 11px; - width: 15px; - height: 15px; - color: var(--muted-foreground); - pointer-events: none; -} -.pn-as-eye { - position: absolute; - right: 6px; - width: 28px; - height: 28px; - display: inline-flex; - align-items: center; - justify-content: center; - border: 0; - border-radius: var(--radius-sm); - background: transparent; - color: var(--muted-foreground); - cursor: pointer; - transition: background-color 0.15s ease, color 0.15s ease; -} -.pn-as-eye svg { - width: 15px; - height: 15px; -} -.pn-as-eye:hover { - background: var(--muted); - color: var(--foreground); -} .pn-as-hint { margin: 0; font-size: 12px; @@ -2706,99 +2040,9 @@ textarea.pn-as-input { padding-top: 16px; border-top: 1px solid var(--border); } -@media (max-width: 767px) { - .pn-as-card { - padding: 20px; - } -} -/* ============================================================ - Toast 与确认弹层(各管理页共用) - ============================================================ */ -.pn-toast { - position: fixed; - right: 24px; - bottom: 24px; - z-index: 50; - display: flex; - align-items: center; - gap: 10px; - padding: 11px 14px; - border: 1px solid var(--success-border); - border-radius: var(--radius-md); - background: var(--success-subtle); - color: var(--success-text); - font-size: 13px; - font-weight: 500; - box-shadow: var(--shadow-sm); - animation: pn-toast-in 0.18s ease; -} -.pn-toast svg { - width: 16px; - height: 16px; - flex: none; -} -.pn-toast.is-error { - border-color: var(--destructive-border); - background: var(--destructive-subtle); - color: var(--destructive-text); -} -@keyframes pn-toast-in { - from { - opacity: 0; - transform: translateY(6px); - } - to { - opacity: 1; - transform: translateY(0); - } -} -@media (max-width: 767px) { - .pn-toast { - left: 16px; - right: 16px; - bottom: 80px; - } -} -/* 确认弹层 */ -.pn-modal-overlay { - position: fixed; - inset: 0; - z-index: 60; - display: grid; - place-items: center; - padding: 16px; - background: color-mix(in srgb, var(--brand-900) 40%, transparent); -} -.pn-modal { - width: 100%; - max-width: 380px; - padding: 24px; - gap: 0; -} -.pn-modal h3 { - margin: 0; - font-size: 15px; - font-weight: 600; - color: var(--foreground); -} -.pn-modal p { - margin: 8px 0 0; - font-size: 13px; - line-height: 1.7; - color: var(--muted-foreground); - overflow-wrap: anywhere; -} -.pn-modal-actions { - display: flex; - justify-content: flex-end; - gap: 8px; - margin-top: 20px; -} -.pn-modal-actions .btn { - height: 34px; - font-size: 13px; -} +/* 操作反馈(notification)与确认弹层(Modal)已由 antd 承接, + 主题与移动端定位见「antd 桥接」段。 */ /* ============================================================ CodeMirror(编辑器,token 化) diff --git a/web/src/lib/utils.ts b/web/src/lib/utils.ts index db65d46..3971a97 100644 --- a/web/src/lib/utils.ts +++ b/web/src/lib/utils.ts @@ -1,7 +1,3 @@ -export function cn(...parts: Array): string { - return parts.filter(Boolean).join(' ') -} - export function formatDate(unix: number): string { return new Date(unix * 1000).toLocaleDateString('zh-CN', { year: 'numeric', diff --git a/web/src/pages/AdminEdit.tsx b/web/src/pages/AdminEdit.tsx index b052de2..8b246ba 100644 --- a/web/src/pages/AdminEdit.tsx +++ b/web/src/pages/AdminEdit.tsx @@ -5,13 +5,14 @@ import { Bold, Calendar, CirclePlus, Code, Eye, EyeOff, Italic, Link2, List, Quote, Save, Table, Trash2, X, } from 'lucide-react' +import { Button, Card, Divider, Input, Switch, Tag } from 'antd' import { api, ApiError } from '../lib/api' import type { NoteItem } from '../lib/api' import { invalidatePublic } from '../lib/invalidate' import { useDocumentTitle } from '../features/site' import MarkdownViewer from '../components/MarkdownViewer' -import Toast from '../components/Toast' import { AdminTopbar } from '../components/Layout' +import { useToast } from '@/hooks/use-toast' import { formatDateISO, slugPreview } from '../lib/utils' // CodeMirror 体积大,单独拆 chunk,编辑器在首帧后异步加载 @@ -50,6 +51,7 @@ export default function AdminEdit() { useDocumentTitle(isNew ? '新建笔记' : '编辑笔记') const navigate = useNavigate() const qc = useQueryClient() + const { toast } = useToast() const { data: existing, isLoading } = useQuery({ queryKey: ['admin', 'note', noteId], @@ -64,7 +66,6 @@ export default function AdminEdit() { const [showPreview, setShowPreview] = useState(false) const [uploadMsg, setUploadMsg] = useState(null) const [tagInput, setTagInput] = useState(null) - const [toast, setToast] = useState<{ msg: string; kind: 'success' | 'error' } | null>(null) const dirtyRef = useRef(false) const slugTouchedRef = useRef(false) const timerRef = useRef | null>(null) @@ -121,7 +122,7 @@ export default function AdminEdit() { setSlugFieldError(null) setErrorMsg(null) dirtyRef.current = false - setToast({ msg: vars.auto ? '已自动保存' : '已保存', kind: 'success' }) + toast({ description: vars.auto ? '已自动保存' : '已保存' }) // 仅失效列表与公共缓存;不失效当前编辑详情(['admin','note',id]), // 防 refetch 回滚保存期间继续输入的内容并抑制下一轮自动保存(评审 round2 P1-4) void qc.invalidateQueries({ queryKey: ['admin', 'notes'] }) @@ -131,7 +132,7 @@ export default function AdminEdit() { } }, onError: (e) => { - setToast({ msg: '保存失败,请重试', kind: 'error' }) + toast({ description: '保存失败,请重试', variant: 'error' }) if (e instanceof ApiError && e.code === 'slug_conflict') { setSlugFieldError(e.message) } else { @@ -147,7 +148,7 @@ export default function AdminEdit() { invalidatePublic(qc) navigate('/admin') }, - onError: () => setToast({ msg: '操作失败,请重试', kind: 'error' }), + onError: () => toast({ description: '操作失败,请重试', variant: 'error' }), }) const canSave = form.title.trim().length > 0 @@ -182,7 +183,7 @@ export default function AdminEdit() { return ( <> -
加载中…
+
加载中…
) } @@ -192,29 +193,28 @@ export default function AdminEdit() { return ( <> - - + {save.isPending ? '保存中…' : isNew ? '保存草稿' : '保存'} +
-
- +
{form.tags.map((t) => ( - + } + onClose={(e) => { + e.preventDefault() + set('tags', form.tags.filter((x) => x !== t)) + }} + aria-label={`标签:${t}`} + > {t} - - + ))} {tagInput === null ? ( - + ) : ( -
- - - - - - -