mirror of
https://github.com/JefferyHcool/BiliNote.git
synced 2026-09-08 17:16:40 +08:00
fix(frontend): 增强锚点链接模糊匹配,兼容 LLM 生成的不一致目录格式
This commit is contained in:
@@ -57,7 +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",
|
"rehype-slug": "5.1.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",
|
||||||
|
|||||||
Generated
+34
-2
@@ -150,8 +150,8 @@ importers:
|
|||||||
specifier: ^6.0.2
|
specifier: ^6.0.2
|
||||||
version: 6.0.3
|
version: 6.0.3
|
||||||
rehype-slug:
|
rehype-slug:
|
||||||
specifier: ^6.0.0
|
specifier: 5.1.0
|
||||||
version: 6.0.0
|
version: 5.1.0
|
||||||
remark-gfm:
|
remark-gfm:
|
||||||
specifier: 3.0.1
|
specifier: 3.0.1
|
||||||
version: 3.0.1
|
version: 3.0.1
|
||||||
@@ -2803,9 +2803,15 @@ packages:
|
|||||||
hast-util-from-parse5@8.0.3:
|
hast-util-from-parse5@8.0.3:
|
||||||
resolution: {integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==}
|
resolution: {integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==}
|
||||||
|
|
||||||
|
hast-util-has-property@2.0.1:
|
||||||
|
resolution: {integrity: sha512-X2+RwZIMTMKpXUzlotatPzWj8bspCymtXH3cfG3iQKV+wPF53Vgaqxi/eLqGck0wKq1kS9nvoB1wchbCPEL8sg==}
|
||||||
|
|
||||||
hast-util-has-property@3.0.0:
|
hast-util-has-property@3.0.0:
|
||||||
resolution: {integrity: sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA==}
|
resolution: {integrity: sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA==}
|
||||||
|
|
||||||
|
hast-util-heading-rank@2.1.1:
|
||||||
|
resolution: {integrity: sha512-iAuRp+ESgJoRFJbSyaqsfvJDY6zzmFoEnL1gtz1+U8gKtGGj1p0CVlysuUAUjq95qlZESHINLThwJzNGmgGZxA==}
|
||||||
|
|
||||||
hast-util-heading-rank@3.0.0:
|
hast-util-heading-rank@3.0.0:
|
||||||
resolution: {integrity: sha512-EJKb8oMUXVHcWZTDepnr+WNbfnXKFNf9duMesmr4S8SXTJBJ9M4Yok08pu9vxdJwdlGRhVumk9mEhkEvKGifwA==}
|
resolution: {integrity: sha512-EJKb8oMUXVHcWZTDepnr+WNbfnXKFNf9duMesmr4S8SXTJBJ9M4Yok08pu9vxdJwdlGRhVumk9mEhkEvKGifwA==}
|
||||||
|
|
||||||
@@ -2842,6 +2848,9 @@ packages:
|
|||||||
hast-util-to-parse5@8.0.1:
|
hast-util-to-parse5@8.0.1:
|
||||||
resolution: {integrity: sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==}
|
resolution: {integrity: sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==}
|
||||||
|
|
||||||
|
hast-util-to-string@2.0.0:
|
||||||
|
resolution: {integrity: sha512-02AQ3vLhuH3FisaMM+i/9sm4OXGSq1UhOOCpTLLQtHdL3tZt7qil69r8M8iDkZYyC0HCFylcYoP+8IO7ddta1A==}
|
||||||
|
|
||||||
hast-util-to-string@3.0.1:
|
hast-util-to-string@3.0.1:
|
||||||
resolution: {integrity: sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==}
|
resolution: {integrity: sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==}
|
||||||
|
|
||||||
@@ -4411,6 +4420,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-L/FO96EOzSA6bzOam4DVu61/PB3AGKcSPXpa53yMIozoxH4qg1+bVZDF8zh1EsuxtSauAhzt5cCnvoplAaSLrw==}
|
resolution: {integrity: sha512-L/FO96EOzSA6bzOam4DVu61/PB3AGKcSPXpa53yMIozoxH4qg1+bVZDF8zh1EsuxtSauAhzt5cCnvoplAaSLrw==}
|
||||||
engines: {node: '>=16.0.0'}
|
engines: {node: '>=16.0.0'}
|
||||||
|
|
||||||
|
rehype-slug@5.1.0:
|
||||||
|
resolution: {integrity: sha512-Gf91dJoXneiorNEnn+Phx97CO7oRMrpi+6r155tTxzGuLtm+QrI4cTwCa9e1rtePdL4i9tSO58PeSS6HWfgsiw==}
|
||||||
|
|
||||||
rehype-slug@6.0.0:
|
rehype-slug@6.0.0:
|
||||||
resolution: {integrity: sha512-lWyvf/jwu+oS5+hL5eClVd3hNdmwM1kAC0BUvEGD19pajQMIzcNUd/k9GsfQ+FfECvX+JE+e9/btsKH0EjJT6A==}
|
resolution: {integrity: sha512-lWyvf/jwu+oS5+hL5eClVd3hNdmwM1kAC0BUvEGD19pajQMIzcNUd/k9GsfQ+FfECvX+JE+e9/btsKH0EjJT6A==}
|
||||||
|
|
||||||
@@ -7878,10 +7890,16 @@ snapshots:
|
|||||||
vfile-location: 5.0.3
|
vfile-location: 5.0.3
|
||||||
web-namespaces: 2.0.1
|
web-namespaces: 2.0.1
|
||||||
|
|
||||||
|
hast-util-has-property@2.0.1: {}
|
||||||
|
|
||||||
hast-util-has-property@3.0.0:
|
hast-util-has-property@3.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/hast': 3.0.4
|
'@types/hast': 3.0.4
|
||||||
|
|
||||||
|
hast-util-heading-rank@2.1.1:
|
||||||
|
dependencies:
|
||||||
|
'@types/hast': 2.3.10
|
||||||
|
|
||||||
hast-util-heading-rank@3.0.0:
|
hast-util-heading-rank@3.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/hast': 3.0.4
|
'@types/hast': 3.0.4
|
||||||
@@ -8004,6 +8022,10 @@ snapshots:
|
|||||||
web-namespaces: 2.0.1
|
web-namespaces: 2.0.1
|
||||||
zwitch: 2.0.4
|
zwitch: 2.0.4
|
||||||
|
|
||||||
|
hast-util-to-string@2.0.0:
|
||||||
|
dependencies:
|
||||||
|
'@types/hast': 2.3.10
|
||||||
|
|
||||||
hast-util-to-string@3.0.1:
|
hast-util-to-string@3.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/hast': 3.0.4
|
'@types/hast': 3.0.4
|
||||||
@@ -10191,6 +10213,16 @@ snapshots:
|
|||||||
unified: 11.0.5
|
unified: 11.0.5
|
||||||
unist-util-visit: 5.1.0
|
unist-util-visit: 5.1.0
|
||||||
|
|
||||||
|
rehype-slug@5.1.0:
|
||||||
|
dependencies:
|
||||||
|
'@types/hast': 2.3.10
|
||||||
|
github-slugger: 2.0.0
|
||||||
|
hast-util-has-property: 2.0.1
|
||||||
|
hast-util-heading-rank: 2.1.1
|
||||||
|
hast-util-to-string: 2.0.0
|
||||||
|
unified: 10.1.2
|
||||||
|
unist-util-visit: 4.1.2
|
||||||
|
|
||||||
rehype-slug@6.0.0:
|
rehype-slug@6.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/hast': 3.0.4
|
'@types/hast': 3.0.4
|
||||||
|
|||||||
@@ -123,7 +123,27 @@ function createMarkdownComponents(baseURL: string) {
|
|||||||
const handleAnchorClick = (e: React.MouseEvent) => {
|
const handleAnchorClick = (e: React.MouseEvent) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
const id = decodeURIComponent(href.slice(1))
|
const id = decodeURIComponent(href.slice(1))
|
||||||
const target = document.getElementById(id)
|
|
||||||
|
// 1. 优先精确匹配 id
|
||||||
|
let target = document.getElementById(id)
|
||||||
|
|
||||||
|
// 2. 精确失败时按 heading 文本模糊匹配
|
||||||
|
// LLM 生成的目录锚点可能和 heading 实际文本不完全一致
|
||||||
|
//(例如 heading 带 *Content-[00:00]* 后缀,目录链接里没有)
|
||||||
|
if (!target) {
|
||||||
|
const normalize = (s: string) =>
|
||||||
|
s.replace(/[-::\s*\[\]]/g, '').toLowerCase()
|
||||||
|
const search = normalize(id)
|
||||||
|
const headings = document.querySelectorAll('h1, h2, h3, h4, h5, h6')
|
||||||
|
for (const h of headings) {
|
||||||
|
const text = h.textContent || ''
|
||||||
|
if (normalize(text).includes(search) || search.includes(normalize(text))) {
|
||||||
|
target = h
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (target) {
|
if (target) {
|
||||||
target.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
target.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user