From d450f4ed4219423f4cf02768e696856131f5d08c Mon Sep 17 00:00:00 2001 From: Max Richter Date: Sun, 26 Jan 2025 02:00:59 +0100 Subject: [PATCH] feat: dont show image duplicate in beginnign of markdown --- islands/Link.tsx | 2 ++ lib/documents.ts | 30 +++++++++++++++-- routes/articles/[name].tsx | 9 +++-- routes/movies/[name].tsx | 10 ++++-- routes/recipes/[name].tsx | 3 +- routes/series/[name].tsx | 10 ++++-- static/global.css | 8 +++++ static/thumbnails.js | 69 ++++++++++++++++++++------------------ 8 files changed, 99 insertions(+), 42 deletions(-) diff --git a/islands/Link.tsx b/islands/Link.tsx index 377a3c0..ea8e4ec 100644 --- a/islands/Link.tsx +++ b/islands/Link.tsx @@ -27,9 +27,11 @@ export function Link( clearTimeout(globalThis.loadingTimeout); delete globalThis.loadingTimeout; setTimeout(() => { + globalThis.dispatchEvent(new CustomEvent("loading-finished")); document.querySelector("main")?.classList.remove("loading"); }, 100); } else { + globalThis.dispatchEvent(new CustomEvent("loading-finished")); document.querySelector("main")?.classList.remove("loading"); } }); diff --git a/lib/documents.ts b/lib/documents.ts index e59a86c..9be0e46 100644 --- a/lib/documents.ts +++ b/lib/documents.ts @@ -132,8 +132,35 @@ export function parseDocument(doc: string) { .parse(doc); } +function removeFrontmatter(doc: string) { + if (doc.trim().startsWith("---")) { + return doc.trim().split("---").filter((s) => s.length).slice(1).join("---"); + } + return doc; +} + +export function removeImage(doc: string, imageUrl?: string) { + if (!imageUrl) { + return doc; + } + // Remove image from content + const first = doc.slice(0, 500); + const second = doc.slice(500); + + // Regex pattern to match the image Markdown syntax with the specific URL + const pattern = new RegExp( + `!\\[.*?\\]\\(${imageUrl.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\)`, + "g", + ); + + // Remove the matched image + const updatedMarkdown = first.replace(pattern, ""); + return updatedMarkdown + second; +} + export function renderMarkdown(doc: string) { - return render(doc, { + return render(removeFrontmatter(doc), { + baseUrl: SILVERBULLET_SERVER, allowMath: true, }); } @@ -172,4 +199,3 @@ export function getTextOfChild(child: DocumentChild): string | undefined { } return; } - diff --git a/routes/articles/[name].tsx b/routes/articles/[name].tsx index b16d5ff..47f916e 100644 --- a/routes/articles/[name].tsx +++ b/routes/articles/[name].tsx @@ -5,7 +5,7 @@ import { KMenu } from "@islands/KMenu.tsx"; import { YoutubePlayer } from "@components/Youtube.tsx"; import { HashTags } from "@components/HashTags.tsx"; import { isYoutubeLink } from "@lib/string.ts"; -import { renderMarkdown } from "@lib/documents.ts"; +import { removeImage, renderMarkdown } from "@lib/documents.ts"; import { RedirectSearchHandler } from "@islands/Search.tsx"; import PageHero from "@components/PageHero.tsx"; import { Star } from "@components/Stars.tsx"; @@ -14,6 +14,9 @@ import { MetaTags } from "@components/MetaTags.tsx"; export const handler: Handlers<{ article: Article; session: unknown }> = { async GET(_, ctx) { const article = await getArticle(ctx.params.name); + if (!article) { + return ctx.renderNotFound(); + } return ctx.render({ article, session: ctx.state.session }); }, }; @@ -25,7 +28,9 @@ export default function Greet( const { author = "", date = "" } = article.meta; - const content = renderMarkdown(article.content); + const content = renderMarkdown( + removeImage(article.content, article.meta.image), + ); return ( ${movie.name}`} context={movie}> diff --git a/routes/recipes/[name].tsx b/routes/recipes/[name].tsx index 961c1b1..23d04e1 100644 --- a/routes/recipes/[name].tsx +++ b/routes/recipes/[name].tsx @@ -60,14 +60,13 @@ function ValidRecipe({ ); } -export default function Greet( +export default function Page( props: PageProps<{ recipe: Recipe; session: Record }>, ) { const { recipe, session } = props.data; const portion = recipe.meta?.portion; const amount = useSignal(portion || 1); - console.log({ recipe }); const subline = [ recipe?.meta?.time && `Duration ${recipe.meta.time}`, diff --git a/routes/series/[name].tsx b/routes/series/[name].tsx index e80123a..251f45a 100644 --- a/routes/series/[name].tsx +++ b/routes/series/[name].tsx @@ -1,7 +1,7 @@ import { Handlers, PageProps } from "$fresh/server.ts"; import { MainLayout } from "@components/layouts/main.tsx"; import { HashTags } from "@components/HashTags.tsx"; -import { renderMarkdown } from "@lib/documents.ts"; +import { removeImage, renderMarkdown } from "@lib/documents.ts"; import { getSeries, Series } from "@lib/resource/series.ts"; import { RedirectSearchHandler } from "@islands/Search.tsx"; import { KMenu } from "@islands/KMenu.tsx"; @@ -12,6 +12,10 @@ import { MetaTags } from "@components/MetaTags.tsx"; export const handler: Handlers<{ serie: Series; session: unknown }> = { async GET(_, ctx) { const serie = await getSeries(ctx.params.name); + + if (!serie) { + return ctx.renderNotFound(); + } return ctx.render({ serie, session: ctx.state.session }); }, }; @@ -23,7 +27,9 @@ export default function Greet( const { author = "", date = "" } = serie.meta; - const content = renderMarkdown(serie.description || ""); + const content = renderMarkdown( + removeImage(serie.description || "", serie.meta.image), + ); return ( ${serie.name}`} context={serie}> diff --git a/static/global.css b/static/global.css index a28cde1..8816f28 100644 --- a/static/global.css +++ b/static/global.css @@ -150,4 +150,12 @@ main.loading { display: inline-flex; margin-left: -26px; margin-right: 12px; + opacity: 0; + transition: opacity 0.2s; +} + +.markdown-body>h1:hover .anchor, +.markdown-body>h2:hover .anchor, +.markdown-body>h3:hover .anchor { + opacity: 1; } diff --git a/static/thumbnails.js b/static/thumbnails.js index 75d00db..3a56334 100644 --- a/static/thumbnails.js +++ b/static/thumbnails.js @@ -226,39 +226,44 @@ function rgbaToDataURL(w, h, rgba) { return "data:image/png;base64," + btoa(String.fromCharCode(...bytes)); } -document.querySelectorAll("[data-thumb]").forEach((entry) => { - const hash = entry.getAttribute("data-thumb"); +function updateThumbnailImages() { + document.querySelectorAll("[data-thumb]").forEach((entry) => { + const hash = entry.getAttribute("data-thumb"); - if (!hash) return; + if (!hash) return; - const decodedString = atob(hash); + const decodedString = atob(hash); - // Create Uint8Array from decoded string - const buffer = new Uint8Array(decodedString.length); - for (let i = 0; i < decodedString.length; i++) { - buffer[i] = decodedString.charCodeAt(i); - } - - const image = thumbHashToRGBA(buffer); - const dataURL = rgbaToDataURL(image.w, image.h, image.rgba); - - entry.style.background = `url(${dataURL})`; - entry.style.backgroundSize = "cover"; - - const child = entry.querySelector("img[data-thumb-img]"); - setTimeout(() => { - const isLoaded = child && child.complete && child.naturalHeight !== 0; - if (child && !isLoaded) { - child.style.opacity = 0; - child.style.filter = "blur(5px)"; - child.addEventListener("load", () => { - child.style.transition = "opacity 0.3s ease, filter 0.6s ease"; - child.style.opacity = 1; - child.style.filter = "blur(0px)"; - setTimeout(() => { - entry.style.background = ""; - }, 400); - }); + // Create Uint8Array from decoded string + const buffer = new Uint8Array(decodedString.length); + for (let i = 0; i < decodedString.length; i++) { + buffer[i] = decodedString.charCodeAt(i); } - }, 50); -}); + + const image = thumbHashToRGBA(buffer); + const dataURL = rgbaToDataURL(image.w, image.h, image.rgba); + + entry.style.background = `url(${dataURL})`; + entry.style.backgroundSize = "cover"; + + const child = entry.querySelector("img[data-thumb-img]"); + setTimeout(() => { + const isLoaded = child && child.complete && child.naturalHeight !== 0; + if (child && !isLoaded) { + child.style.opacity = 0; + child.style.filter = "blur(5px)"; + child.addEventListener("load", () => { + child.style.transition = "opacity 0.3s ease, filter 0.6s ease"; + child.style.opacity = 1; + child.style.filter = "blur(0px)"; + setTimeout(() => { + entry.style.background = ""; + }, 400); + }); + } + }, 50); + }); +} + +globalThis.addEventListener("load", updateThumbnailImages); +globalThis.addEventListener("loading-finished", updateThumbnailImages);