fix(frontend): 修复 Markdown 目录锚点跳转与 Tauri 路由

- 安装 rehype-slug 插件,自动为 heading 生成 id,解决目录链接无锚点目标的问题
- 自定义 <a> 组件处理内部锚点链接,阻止默认刷新行为并使用 scrollIntoView 平滑滚动
- Tauri 桌面端改用 HashRouter,避免刷新时 404 及错误打开外部浏览器

fixes #xxx

Co-Authored-By: HAPI <noreply@hapi.run>
This commit is contained in:
techotaku39
2026-05-24 02:53:08 +08:00
parent 717df2af7b
commit ebdb254fc6
4 changed files with 98 additions and 77 deletions

View File

@@ -57,6 +57,7 @@
"react-router-dom": "^7.5.1", "react-router-dom": "^7.5.1",
"react-syntax-highlighter": "^15.6.1", "react-syntax-highlighter": "^15.6.1",
"rehype-katex": "^6.0.2", "rehype-katex": "^6.0.2",
"rehype-slug": "^6.0.0",
"remark-gfm": "3.0.1", "remark-gfm": "3.0.1",
"remark-math": "^5.1.1", "remark-math": "^5.1.1",
"sonner": "^2.0.3", "sonner": "^2.0.3",

View File

@@ -13,7 +13,7 @@ importers:
version: 2.4.0(antd@5.29.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) version: 2.4.0(antd@5.29.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@hookform/resolvers': '@hookform/resolvers':
specifier: ^5.0.1 specifier: ^5.0.1
version: 5.2.2(react-hook-form@7.72.0(react@19.2.4)) version: 5.4.0(react-hook-form@7.72.0(react@19.2.4))
'@lobehub/icons': '@lobehub/icons':
specifier: ^1.97.1 specifier: ^1.97.1
version: 1.98.0(@babel/core@7.29.0)(@types/mdast@4.0.4)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(antd@5.29.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(framer-motion@12.38.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(micromark-util-types@2.0.2)(micromark@4.0.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) version: 1.98.0(@babel/core@7.29.0)(@types/mdast@4.0.4)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(antd@5.29.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(framer-motion@12.38.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(micromark-util-types@2.0.2)(micromark@4.0.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
@@ -52,7 +52,7 @@ importers:
version: 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) version: 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@tailwindcss/vite': '@tailwindcss/vite':
specifier: ^4.1.3 specifier: ^4.1.3
version: 4.2.2(vite@6.4.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3)) version: 4.2.2(vite@6.4.2(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3))
'@tauri-apps/api': '@tauri-apps/api':
specifier: ^2.11.0 specifier: ^2.11.0
version: 2.11.0 version: 2.11.0
@@ -61,7 +61,7 @@ importers:
version: 2.3.5 version: 2.3.5
'@uiw/react-markdown-preview': '@uiw/react-markdown-preview':
specifier: ^5.1.3 specifier: ^5.1.3
version: 5.1.5(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) version: 5.2.1(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
antd: antd:
specifier: ^5.24.8 specifier: ^5.24.8
version: 5.29.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4) version: 5.29.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
@@ -76,13 +76,13 @@ importers:
version: 2.1.1 version: 2.1.1
fuse.js: fuse.js:
specifier: ^7.1.0 specifier: ^7.1.0
version: 7.1.0 version: 7.3.0
github-markdown-css: github-markdown-css:
specifier: ^5.8.1 specifier: ^5.8.1
version: 5.9.0 version: 5.9.0
idb-keyval: idb-keyval:
specifier: ^6.2.2 specifier: ^6.2.2
version: 6.2.2 version: 6.2.4
jszip: jszip:
specifier: ^3.10.1 specifier: ^3.10.1
version: 3.10.1 version: 3.10.1
@@ -149,6 +149,9 @@ importers:
rehype-katex: rehype-katex:
specifier: ^6.0.2 specifier: ^6.0.2
version: 6.0.3 version: 6.0.3
rehype-slug:
specifier: ^6.0.0
version: 6.0.0
remark-gfm: remark-gfm:
specifier: 3.0.1 specifier: 3.0.1
version: 3.0.1 version: 3.0.1
@@ -169,7 +172,7 @@ importers:
version: 1.4.0 version: 1.4.0
uuid: uuid:
specifier: ^11.1.0 specifier: ^11.1.0
version: 11.1.0 version: 11.1.1
zod: zod:
specifier: ^3.24.2 specifier: ^3.24.2
version: 3.25.76 version: 3.25.76
@@ -197,7 +200,7 @@ importers:
version: 19.2.3(@types/react@19.2.14) version: 19.2.3(@types/react@19.2.14)
'@vitejs/plugin-react': '@vitejs/plugin-react':
specifier: ^4.3.4 specifier: ^4.3.4
version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3)) version: 4.7.0(vite@6.4.2(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3))
autoprefixer: autoprefixer:
specifier: ^10.4.21 specifier: ^10.4.21
version: 10.4.27(postcss@8.5.8) version: 10.4.27(postcss@8.5.8)
@@ -227,7 +230,7 @@ importers:
version: 8.57.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.7.3) version: 8.57.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.7.3)
vite: vite:
specifier: ^6.2.0 specifier: ^6.2.0
version: 6.4.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3) version: 6.4.2(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3)
packages: packages:
@@ -732,8 +735,8 @@ packages:
react: ^16 || ^17 || ^18 || ^19 react: ^16 || ^17 || ^18 || ^19
react-dom: ^16 || ^17 || ^18 || ^19 react-dom: ^16 || ^17 || ^18 || ^19
'@hookform/resolvers@5.2.2': '@hookform/resolvers@5.4.0':
resolution: {integrity: sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA==} resolution: {integrity: sha512-EIsqr/t/qbinPIhGjMdtvutIN1Kk4uwbROE9/UQ93CAVGR7GkA7Y92+fX80OzXi/OB67jVFYwKGO1WzkxmkFZw==}
peerDependencies: peerDependencies:
react-hook-form: ^7.55.0 react-hook-form: ^7.55.0
@@ -1786,6 +1789,9 @@ packages:
'@types/estree@1.0.8': '@types/estree@1.0.8':
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
'@types/estree@1.0.9':
resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
'@types/geojson@7946.0.16': '@types/geojson@7946.0.16':
resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==}
@@ -1910,8 +1916,8 @@ packages:
'@uiw/copy-to-clipboard@1.0.20': '@uiw/copy-to-clipboard@1.0.20':
resolution: {integrity: sha512-IFQhS62CLNon1YgYJTEzXR2N3WVXg7V1FaBRDLMlzU6JY5X6Hr3OPAcw4WNoKcz2XcFD6XCgwEjlsmj+JA0mWA==} resolution: {integrity: sha512-IFQhS62CLNon1YgYJTEzXR2N3WVXg7V1FaBRDLMlzU6JY5X6Hr3OPAcw4WNoKcz2XcFD6XCgwEjlsmj+JA0mWA==}
'@uiw/react-markdown-preview@5.1.5': '@uiw/react-markdown-preview@5.2.1':
resolution: {integrity: sha512-DNOqx1a6gJR7Btt57zpGEKTfHRlb7rWbtctMRO2f82wWcuoJsxPBrM+JWebDdOD0LfD8oe2CQvW2ICQJKHQhZg==} resolution: {integrity: sha512-JjvcHveT6glhlJYJx1XGBZij6wkw+VwREV6Z6m/GpsjPPdLjF1x8nlPBSB/ATyUF4lD7C8ttMkCqVH9N9XMgEA==}
peerDependencies: peerDependencies:
react: '>=16.8.0' react: '>=16.8.0'
react-dom: '>=16.8.0' react-dom: '>=16.8.0'
@@ -1919,6 +1925,9 @@ packages:
'@ungap/structured-clone@1.3.0': '@ungap/structured-clone@1.3.0':
resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
'@ungap/structured-clone@1.3.1':
resolution: {integrity: sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==}
'@upsetjs/venn.js@2.0.0': '@upsetjs/venn.js@2.0.0':
resolution: {integrity: sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==} resolution: {integrity: sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==}
@@ -2689,8 +2698,8 @@ packages:
function-bind@1.1.2: function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
fuse.js@7.1.0: fuse.js@7.3.0:
resolution: {integrity: sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ==} resolution: {integrity: sha512-plz8RVjfcDedTGfVngWH1jmJvBvAwi1v2jecfDerbEnMcmOYUEEwKFTHbNoCiYyzaK2Ws8lABkTCcRSqCY1q4w==}
engines: {node: '>=10'} engines: {node: '>=10'}
gensync@1.0.0-beta.2: gensync@1.0.0-beta.2:
@@ -2883,8 +2892,8 @@ packages:
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
idb-keyval@6.2.2: idb-keyval@6.2.4:
resolution: {integrity: sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==} resolution: {integrity: sha512-D/NzHWUmYJGXi++z67aMSrnisb9A3621CyRK5G89JyTlN13C8xf0g04DLxUKMufPem3e3L2JAXR6Z00OWy183Q==}
ignore@5.3.2: ignore@5.3.2:
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
@@ -4228,12 +4237,6 @@ packages:
'@types/react': '>=16' '@types/react': '>=16'
react: '>=16' react: '>=16'
react-markdown@9.0.3:
resolution: {integrity: sha512-Yk7Z94dbgYTOrdk41Z74GoKA7rThnsbbqBTRYuxoe08qvfQ9tJVhmAKw6BJS/ZORG7kTy/s1QvYzSuaoBA1qfw==}
peerDependencies:
'@types/react': '>=18'
react: '>=18'
react-medium-image-zoom@5.4.1: react-medium-image-zoom@5.4.1:
resolution: {integrity: sha512-DD2iZYaCfAwiQGR8AN62r/cDJYoXhezlYJc5HY4TzBUGuGge43CptG0f7m0PEIM72aN6GfpjohvY1yYdtCJB7g==} resolution: {integrity: sha512-DD2iZYaCfAwiQGR8AN62r/cDJYoXhezlYJc5HY4TzBUGuGge43CptG0f7m0PEIM72aN6GfpjohvY1yYdtCJB7g==}
peerDependencies: peerDependencies:
@@ -4372,8 +4375,8 @@ packages:
regex@6.1.0: regex@6.1.0:
resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==} resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==}
rehype-attr@3.0.3: rehype-attr@4.0.0:
resolution: {integrity: sha512-Up50Xfra8tyxnkJdCzLBIBtxOcB2M1xdeKe1324U06RAvSjYm7ULSeoM+b/nYPQPVd7jsXJ9+39IG1WAJPXONw==} resolution: {integrity: sha512-tANn9EmhG4mEZlNdDDRKuS0OXPDvc6P6OjJ1yApzOjIdCvKNLiuU2HdMSLTpiVi3D/FyLK6B+ZZ8PYtRxiGg7Q==}
engines: {node: '>=16'} engines: {node: '>=16'}
rehype-autolink-headings@7.1.0: rehype-autolink-headings@7.1.0:
@@ -4822,12 +4825,12 @@ packages:
util-deprecate@1.0.2: util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
uuid@11.1.0: uuid@11.1.1:
resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} resolution: {integrity: sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==}
hasBin: true hasBin: true
uuid@13.0.0: uuid@13.0.2:
resolution: {integrity: sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==} resolution: {integrity: sha512-vzi9uRZ926x4XV73S/4qQaTwPXM2JBj6/6lI/byHH1jOpCzb0zDbfytgA9LcN/hzb2l7WQSQnxITOVx5un/wGw==}
hasBin: true hasBin: true
uvu@0.5.6: uvu@0.5.6:
@@ -4856,8 +4859,8 @@ packages:
vfile@6.0.3: vfile@6.0.3:
resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
vite@6.4.1: vite@6.4.2:
resolution: {integrity: sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==} resolution: {integrity: sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
hasBin: true hasBin: true
peerDependencies: peerDependencies:
@@ -5534,7 +5537,7 @@ snapshots:
react: 19.2.4 react: 19.2.4
react-dom: 19.2.4(react@19.2.4) react-dom: 19.2.4(react@19.2.4)
'@hookform/resolvers@5.2.2(react-hook-form@7.72.0(react@19.2.4))': '@hookform/resolvers@5.4.0(react-hook-form@7.72.0(react@19.2.4))':
dependencies: dependencies:
'@standard-schema/utils': 0.3.0 '@standard-schema/utils': 0.3.0
react-hook-form: 7.72.0(react@19.2.4) react-hook-form: 7.72.0(react@19.2.4)
@@ -5725,7 +5728,7 @@ snapshots:
unified: 11.0.5 unified: 11.0.5
url-join: 5.0.0 url-join: 5.0.0
use-merge-value: 1.2.0(react@19.2.4) use-merge-value: 1.2.0(react@19.2.4)
uuid: 13.0.0 uuid: 13.0.2
transitivePeerDependencies: transitivePeerDependencies:
- '@babel/core' - '@babel/core'
- '@types/mdast' - '@types/mdast'
@@ -6456,12 +6459,12 @@ snapshots:
postcss: 8.5.8 postcss: 8.5.8
tailwindcss: 4.2.2 tailwindcss: 4.2.2
'@tailwindcss/vite@4.2.2(vite@6.4.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3))': '@tailwindcss/vite@4.2.2(vite@6.4.2(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3))':
dependencies: dependencies:
'@tailwindcss/node': 4.2.2 '@tailwindcss/node': 4.2.2
'@tailwindcss/oxide': 4.2.2 '@tailwindcss/oxide': 4.2.2
tailwindcss: 4.2.2 tailwindcss: 4.2.2
vite: 6.4.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3) vite: 6.4.2(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3)
'@tauri-apps/api@2.11.0': {} '@tauri-apps/api@2.11.0': {}
@@ -6664,6 +6667,8 @@ snapshots:
'@types/estree@1.0.8': {} '@types/estree@1.0.8': {}
'@types/estree@1.0.9': {}
'@types/geojson@7946.0.16': {} '@types/geojson@7946.0.16': {}
'@types/hast@2.3.10': '@types/hast@2.3.10':
@@ -6811,14 +6816,14 @@ snapshots:
'@uiw/copy-to-clipboard@1.0.20': {} '@uiw/copy-to-clipboard@1.0.20': {}
'@uiw/react-markdown-preview@5.1.5(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': '@uiw/react-markdown-preview@5.2.1(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
dependencies: dependencies:
'@babel/runtime': 7.29.2 '@babel/runtime': 7.29.2
'@uiw/copy-to-clipboard': 1.0.20 '@uiw/copy-to-clipboard': 1.0.20
react: 19.2.4 react: 19.2.4
react-dom: 19.2.4(react@19.2.4) react-dom: 19.2.4(react@19.2.4)
react-markdown: 9.0.3(@types/react@19.2.14)(react@19.2.4) react-markdown: 10.1.0(@types/react@19.2.14)(react@19.2.4)
rehype-attr: 3.0.3 rehype-attr: 4.0.0
rehype-autolink-headings: 7.1.0 rehype-autolink-headings: 7.1.0
rehype-ignore: 2.0.3 rehype-ignore: 2.0.3
rehype-prism-plus: 2.0.0 rehype-prism-plus: 2.0.0
@@ -6834,6 +6839,8 @@ snapshots:
'@ungap/structured-clone@1.3.0': {} '@ungap/structured-clone@1.3.0': {}
'@ungap/structured-clone@1.3.1': {}
'@upsetjs/venn.js@2.0.0': '@upsetjs/venn.js@2.0.0':
optionalDependencies: optionalDependencies:
d3-selection: 3.0.0 d3-selection: 3.0.0
@@ -6846,7 +6853,7 @@ snapshots:
'@use-gesture/core': 10.3.1 '@use-gesture/core': 10.3.1
react: 19.2.4 react: 19.2.4
'@vitejs/plugin-react@4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3))': '@vitejs/plugin-react@4.7.0(vite@6.4.2(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3))':
dependencies: dependencies:
'@babel/core': 7.29.0 '@babel/core': 7.29.0
'@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0)
@@ -6854,7 +6861,7 @@ snapshots:
'@rolldown/pluginutils': 1.0.0-beta.27 '@rolldown/pluginutils': 1.0.0-beta.27
'@types/babel__core': 7.20.5 '@types/babel__core': 7.20.5
react-refresh: 0.17.0 react-refresh: 0.17.0
vite: 6.4.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3) vite: 6.4.2(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -7630,7 +7637,7 @@ snapshots:
estree-util-attach-comments@3.0.0: estree-util-attach-comments@3.0.0:
dependencies: dependencies:
'@types/estree': 1.0.8 '@types/estree': 1.0.9
estree-util-build-jsx@3.0.1: estree-util-build-jsx@3.0.1:
dependencies: dependencies:
@@ -7742,7 +7749,7 @@ snapshots:
function-bind@1.1.2: {} function-bind@1.1.2: {}
fuse.js@7.1.0: {} fuse.js@7.3.0: {}
gensync@1.0.0-beta.2: {} gensync@1.0.0-beta.2: {}
@@ -7934,7 +7941,7 @@ snapshots:
hast-util-to-estree@3.1.3: hast-util-to-estree@3.1.3:
dependencies: dependencies:
'@types/estree': 1.0.8 '@types/estree': 1.0.9
'@types/estree-jsx': 1.0.5 '@types/estree-jsx': 1.0.5
'@types/hast': 3.0.4 '@types/hast': 3.0.4
comma-separated-tokens: 2.0.3 comma-separated-tokens: 2.0.3
@@ -7969,7 +7976,7 @@ snapshots:
hast-util-to-jsx-runtime@2.3.6: hast-util-to-jsx-runtime@2.3.6:
dependencies: dependencies:
'@types/estree': 1.0.8 '@types/estree': 1.0.9
'@types/hast': 3.0.4 '@types/hast': 3.0.4
'@types/unist': 3.0.3 '@types/unist': 3.0.3
comma-separated-tokens: 2.0.3 comma-separated-tokens: 2.0.3
@@ -8070,7 +8077,7 @@ snapshots:
dependencies: dependencies:
safer-buffer: 2.1.2 safer-buffer: 2.1.2
idb-keyval@6.2.2: {} idb-keyval@6.2.4: {}
ignore@5.3.2: {} ignore@5.3.2: {}
@@ -8690,7 +8697,7 @@ snapshots:
dependencies: dependencies:
'@types/hast': 3.0.4 '@types/hast': 3.0.4
'@types/mdast': 4.0.4 '@types/mdast': 4.0.4
'@ungap/structured-clone': 1.3.0 '@ungap/structured-clone': 1.3.1
devlop: 1.1.0 devlop: 1.1.0
micromark-util-sanitize-uri: 2.0.1 micromark-util-sanitize-uri: 2.0.1
trim-lines: 3.0.1 trim-lines: 3.0.1
@@ -8760,7 +8767,7 @@ snapshots:
roughjs: 4.6.6 roughjs: 4.6.6
stylis: 4.3.6 stylis: 4.3.6
ts-dedent: 2.2.0 ts-dedent: 2.2.0
uuid: 11.1.0 uuid: 11.1.1
micromark-core-commonmark@1.1.0: micromark-core-commonmark@1.1.0:
dependencies: dependencies:
@@ -8957,7 +8964,7 @@ snapshots:
micromark-extension-mdx-expression@3.0.1: micromark-extension-mdx-expression@3.0.1:
dependencies: dependencies:
'@types/estree': 1.0.8 '@types/estree': 1.0.9
devlop: 1.1.0 devlop: 1.1.0
micromark-factory-mdx-expression: 2.0.3 micromark-factory-mdx-expression: 2.0.3
micromark-factory-space: 2.0.1 micromark-factory-space: 2.0.1
@@ -8968,7 +8975,7 @@ snapshots:
micromark-extension-mdx-jsx@3.0.2: micromark-extension-mdx-jsx@3.0.2:
dependencies: dependencies:
'@types/estree': 1.0.8 '@types/estree': 1.0.9
devlop: 1.1.0 devlop: 1.1.0
estree-util-is-identifier-name: 3.0.0 estree-util-is-identifier-name: 3.0.0
micromark-factory-mdx-expression: 2.0.3 micromark-factory-mdx-expression: 2.0.3
@@ -8985,7 +8992,7 @@ snapshots:
micromark-extension-mdxjs-esm@3.0.0: micromark-extension-mdxjs-esm@3.0.0:
dependencies: dependencies:
'@types/estree': 1.0.8 '@types/estree': 1.0.9
devlop: 1.1.0 devlop: 1.1.0
micromark-core-commonmark: 2.0.3 micromark-core-commonmark: 2.0.3
micromark-util-character: 2.1.1 micromark-util-character: 2.1.1
@@ -9034,7 +9041,7 @@ snapshots:
micromark-factory-mdx-expression@2.0.3: micromark-factory-mdx-expression@2.0.3:
dependencies: dependencies:
'@types/estree': 1.0.8 '@types/estree': 1.0.9
devlop: 1.1.0 devlop: 1.1.0
micromark-factory-space: 2.0.1 micromark-factory-space: 2.0.1
micromark-util-character: 2.1.1 micromark-util-character: 2.1.1
@@ -9150,7 +9157,7 @@ snapshots:
micromark-util-events-to-acorn@2.0.3: micromark-util-events-to-acorn@2.0.3:
dependencies: dependencies:
'@types/estree': 1.0.8 '@types/estree': 1.0.9
'@types/unist': 3.0.3 '@types/unist': 3.0.3
devlop: 1.1.0 devlop: 1.1.0
estree-util-visit: 2.0.0 estree-util-visit: 2.0.0
@@ -9936,23 +9943,6 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
react-markdown@9.0.3(@types/react@19.2.14)(react@19.2.4):
dependencies:
'@types/hast': 3.0.4
'@types/react': 19.2.14
devlop: 1.1.0
hast-util-to-jsx-runtime: 2.3.6
html-url-attributes: 3.0.1
mdast-util-to-hast: 13.2.1
react: 19.2.4
remark-parse: 11.0.0
remark-rehype: 11.1.2
unified: 11.0.5
unist-util-visit: 5.1.0
vfile: 6.0.3
transitivePeerDependencies:
- supports-color
react-medium-image-zoom@5.4.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4): react-medium-image-zoom@5.4.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
dependencies: dependencies:
react: 19.2.4 react: 19.2.4
@@ -10078,7 +10068,7 @@ snapshots:
recma-parse@1.0.0: recma-parse@1.0.0:
dependencies: dependencies:
'@types/estree': 1.0.8 '@types/estree': 1.0.9
esast-util-from-js: 2.0.1 esast-util-from-js: 2.0.1
unified: 11.0.5 unified: 11.0.5
vfile: 6.0.3 vfile: 6.0.3
@@ -10120,7 +10110,7 @@ snapshots:
dependencies: dependencies:
regex-utilities: 2.3.0 regex-utilities: 2.3.0
rehype-attr@3.0.3: rehype-attr@4.0.0:
dependencies: dependencies:
unified: 11.0.5 unified: 11.0.5
unist-util-visit: 5.0.0 unist-util-visit: 5.0.0
@@ -10696,9 +10686,9 @@ snapshots:
util-deprecate@1.0.2: {} util-deprecate@1.0.2: {}
uuid@11.1.0: {} uuid@11.1.1: {}
uuid@13.0.0: {} uuid@13.0.2: {}
uvu@0.5.6: uvu@0.5.6:
dependencies: dependencies:
@@ -10741,7 +10731,7 @@ snapshots:
'@types/unist': 3.0.3 '@types/unist': 3.0.3
vfile-message: 4.0.3 vfile-message: 4.0.3
vite@6.4.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3): vite@6.4.2(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3):
dependencies: dependencies:
esbuild: 0.25.12 esbuild: 0.25.12
fdir: 6.5.0(picomatch@4.0.3) fdir: 6.5.0(picomatch@4.0.3)

View File

@@ -1,6 +1,6 @@
import './App.css' import './App.css'
import { lazy, Suspense, useEffect } from 'react' import { lazy, Suspense, useEffect } from 'react'
import { BrowserRouter, Navigate, Routes, Route } from 'react-router-dom' import { BrowserRouter, HashRouter, Navigate, Routes, Route } from 'react-router-dom'
import { useTaskPolling } from '@/hooks/useTaskPolling.ts' import { useTaskPolling } from '@/hooks/useTaskPolling.ts'
import { useCheckBackend } from '@/hooks/useCheckBackend.ts' import { useCheckBackend } from '@/hooks/useCheckBackend.ts'
import { systemCheck } from '@/services/system.ts' import { systemCheck } from '@/services/system.ts'
@@ -57,12 +57,16 @@ function App() {
) )
} }
// 桌面端使用 HashRouter 避免刷新 404Web 端继续使用 BrowserRouter
const isTauri = typeof window !== 'undefined' && '__TAURI_INTERNALS__' in window
const Router = isTauri ? HashRouter : BrowserRouter
// 后端已初始化,渲染主应用 // 后端已初始化,渲染主应用
return ( return (
<> <>
<StartupBanner /> <StartupBanner />
<BackendHealthIndicator /> <BackendHealthIndicator />
<BrowserRouter> <Router>
<Suspense fallback={<div className="flex h-screen items-center justify-center"></div>}> <Suspense fallback={<div className="flex h-screen items-center justify-center"></div>}>
<Routes> <Routes>
<Route path="/onboarding" element={<Onboarding />} /> <Route path="/onboarding" element={<Onboarding />} />
@@ -86,7 +90,7 @@ function App() {
</Route> </Route>
</Routes> </Routes>
</Suspense> </Suspense>
</BrowserRouter> </Router>
</> </>
) )
} }

View File

@@ -14,6 +14,7 @@ import 'react-medium-image-zoom/dist/styles.css'
import gfm from 'remark-gfm' import gfm from 'remark-gfm'
import remarkMath from 'remark-math' import remarkMath from 'remark-math'
import rehypeKatex from 'rehype-katex' import rehypeKatex from 'rehype-katex'
import rehypeSlug from 'rehype-slug'
import 'katex/dist/katex.min.css' import 'katex/dist/katex.min.css'
import 'github-markdown-css/github-markdown-light.css' import 'github-markdown-css/github-markdown-light.css'
import { ScrollArea } from '@/components/ui/scroll-area.tsx' import { ScrollArea } from '@/components/ui/scroll-area.tsx'
@@ -47,7 +48,7 @@ const steps = [
] ]
const remarkPlugins = [gfm, remarkMath] const remarkPlugins = [gfm, remarkMath]
const rehypePlugins = [rehypeKatex] const rehypePlugins = [rehypeKatex, rehypeSlug]
/** /**
* 构建 ReactMarkdown components 对象baseURL 用于修正图片路径。 * 构建 ReactMarkdown components 对象baseURL 用于修正图片路径。
@@ -117,6 +118,31 @@ function createMarkdownComponents(baseURL: string) {
) )
} }
// 处理笔记内部锚点链接(如目录跳转)
if (href?.startsWith('#')) {
const handleAnchorClick = (e: React.MouseEvent) => {
e.preventDefault()
const id = decodeURIComponent(href.slice(1))
const target = document.getElementById(id)
if (target) {
target.scrollIntoView({ behavior: 'smooth', block: 'start' })
} else {
toast.error('未找到对应章节')
}
}
return (
<a
href={href}
onClick={handleAnchorClick}
className="text-primary hover:text-primary/80 inline-flex items-center gap-0.5 font-medium underline underline-offset-4"
{...props}
>
{children}
</a>
)
}
return ( return (
<a <a
href={href} href={href}