feat: add custom body link component

This commit is contained in:
Thomas Bishop 2025-09-26 14:33:07 +01:00
parent f22f380b96
commit 47abc44506
8 changed files with 314 additions and 247 deletions

42
package-lock.json generated
View file

@ -10,7 +10,7 @@
"dependencies": {
"@radix-ui/react-collapsible": "^1.1.7",
"@radix-ui/react-dialog": "^1.1.10",
"@radix-ui/react-hover-card": "^1.1.14",
"@radix-ui/react-hover-card": "^1.1.15",
"@radix-ui/react-label": "^2.1.7",
"@radix-ui/react-separator": "^1.1.4",
"@radix-ui/react-slot": "^1.2.3",
@ -1300,18 +1300,18 @@
}
},
"node_modules/@radix-ui/react-hover-card": {
"version": "1.1.14",
"resolved": "https://registry.npmjs.org/@radix-ui/react-hover-card/-/react-hover-card-1.1.14.tgz",
"integrity": "sha512-CPYZ24Mhirm+g6D8jArmLzjYu4Eyg3TTUHswR26QgzXBHBe64BO/RHOJKzmF/Dxb4y4f9PKyJdwm/O/AhNkb+Q==",
"version": "1.1.15",
"resolved": "https://registry.npmjs.org/@radix-ui/react-hover-card/-/react-hover-card-1.1.15.tgz",
"integrity": "sha512-qgTkjNT1CfKMoP0rcasmlH2r1DAiYicWsDsufxl940sT2wHNEWWv6FMWIQXWhVdmC1d/HYfbhQx60KYyAtKxjg==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.2",
"@radix-ui/primitive": "1.1.3",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-dismissable-layer": "1.1.10",
"@radix-ui/react-popper": "1.2.7",
"@radix-ui/react-dismissable-layer": "1.1.11",
"@radix-ui/react-popper": "1.2.8",
"@radix-ui/react-portal": "1.1.9",
"@radix-ui/react-presence": "1.1.4",
"@radix-ui/react-presence": "1.1.5",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-controllable-state": "1.2.2"
},
@ -1330,6 +1330,12 @@
}
}
},
"node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/primitive": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
"integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==",
"license": "MIT"
},
"node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-arrow": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz",
@ -1354,12 +1360,12 @@
}
},
"node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-dismissable-layer": {
"version": "1.1.10",
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.10.tgz",
"integrity": "sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ==",
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz",
"integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.2",
"@radix-ui/primitive": "1.1.3",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-callback-ref": "1.1.1",
@ -1381,9 +1387,9 @@
}
},
"node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-popper": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.7.tgz",
"integrity": "sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ==",
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz",
"integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==",
"license": "MIT",
"dependencies": {
"@floating-ui/react-dom": "^2.0.0",
@ -1437,9 +1443,9 @@
}
},
"node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-presence": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.4.tgz",
"integrity": "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==",
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz",
"integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2",

View file

@ -12,7 +12,7 @@
"dependencies": {
"@radix-ui/react-collapsible": "^1.1.7",
"@radix-ui/react-dialog": "^1.1.10",
"@radix-ui/react-hover-card": "^1.1.14",
"@radix-ui/react-hover-card": "^1.1.15",
"@radix-ui/react-label": "^2.1.7",
"@radix-ui/react-separator": "^1.1.4",
"@radix-ui/react-slot": "^1.2.3",

View file

@ -4,12 +4,12 @@ code {
font-family: "JetBrains Mono";
}
pre > code {
pre>code {
padding: 0.5rem;
font-size: 14px;
}
h2 > code {
h2>code {
font-size: 1rem;
}
@ -23,3 +23,12 @@ button[data-state="active"] {
box-shadow:
var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
/* .HoverCardContent { */
/* width: 300px; */
/* max-height: 500px; */
/* } */
/* .HoverCardContent { */
/* transform-origin: 10px; */
/* } */

View file

@ -0,0 +1,50 @@
import { useEffect, useState } from "react"
import { useQueryClient } from "@tanstack/react-query"
import api from "@/api/eolas-api"
export default function BodyLink({ link, children }) {
const [entryExists, setEntryExists] = useState(false)
const path = link.split("/").pop().split(".")[0]
const queryClient = useQueryClient()
useEffect(() => {
const fetchEntryPreview = async () => {
const cachedEntry = queryClient.getQueryData([`entry_${path}`])
if (cachedEntry) {
setEntryExists(true)
console.info("INFO: Entry exists in cache.")
} else {
try {
const remoteEntry = await queryClient.fetchQuery({
queryKey: [`entry_${path}`],
queryFn: () => api.get(`/entries/${path}`).then((res) => res.data),
})
setEntryExists(true)
console.info("INFO: Entry exists on remote.")
} catch (error) {
console.log(`INFO: Could not fetch entry ${path} ${error}`)
setEntryExists(false)
}
}
}
fetchEntryPreview()
}, [path, queryClient])
if (entryExists) {
return (
<a
className="text-foreground underline-offset-4 underline hover:text-gray-700 dark:hover:text-green-300"
href={`/entries/${path}`}
>
{children}
</a>
)
} else
return (
<a className="text-red-500 line-through" href={`/entries/${path}`}>
{children}
</a>
)
}

View file

@ -5,6 +5,7 @@ import remarkMath from "remark-math"
import rehypeKatex from "rehype-katex"
import "katex/dist/katex.min.css"
import { Skeleton } from "@/components/ui/skeleton"
import BodyLink from "./BodyLink"
const EntryLoadingSkeleton = () => {
return (
@ -59,11 +60,6 @@ export default function EntryBody({ body, isLoading }) {
ol: ({ children }) => (
<ol className="list-decimal ml-10 mb-4 space-y-1">{children}</ol>
),
a: ({ href, children }) => (
<a href={href} target="_blank" rel="noopener noreferrer">
{children}
</a>
),
table: ({ children }) => (
<table className="w-full mb-4 text-sm">{children}</table>
),
@ -94,7 +90,10 @@ export default function EntryBody({ body, isLoading }) {
{children}
</code>
),
img: ({ children, src }) => <ImagePreprocessor src={src} />,
img: ({ src }) => <ImagePreprocessor src={src} />,
a: ({ href, children }) => {
return <BodyLink link={href} children={children} />
},
}}
>
{body}

View file

@ -19,7 +19,6 @@ export default function EntryReferences({ entryTitle }) {
queryFn: () => api.get(`/entries/outlinks/${entryTitle}`).then((res) => res.data),
})
console.log(backlinks)
return (
<div className="w-full flex flex-row justify-between gap-3">
<div className="w-full">

View file

@ -1,6 +1,10 @@
@import "tailwindcss";
@import "tw-animate-css";
html {
overflow-y: hidden;
}
@custom-variant dark (&:is(.dark *));
@theme inline {