Repository navigation
Conversation
martinkoenig-bbv
left a comment
There was a problem hiding this comment.
Verifikation
npm test unter Node 22 ausführen, z. B. npx -y -p node@22 node --test "plugins/**/*.test.js".
npm run typecheck.
npm run build ohne neue Warnungen, dann npm run serve. Prüfen: Posts mit 0, 1, 2 und 3 Related Posts; Hover auf allen vier Share-Buttons; „Copy link“; Breiten unter 768 px, 769–1200 px und über 1200 px.
Pagefind-Suche: Titel aus „Related Articles“ dürfen nicht als Treffer im aktuellen Post erscheinen.
| padding: 0.35rem 0.75rem; | ||
| font-size: 0.85rem; | ||
| cursor: pointer; | ||
| text-decoration: none; |
There was a problem hiding this comment.
Claude Code meint:
Share-Buttons sehen uneinheitlich aus — [ShareButtons/styles.module.css:19] .button { text-decoration: none } (Spezifität 0,1,0) verliert gegen das globale a:hover { text-decoration: underline } in [custom.css:112] (0,1,1). „LinkedIn“ und „X“ werden beim Hover unterstrichen, die beiden nicht. Zudem erben die Seitenschrift nicht. → Fix: Die Infima-Klassen button button--sm button--outline button--secondary verwenden. Sie stylen und identisch und unterdrücken die Unterstreichung. Der Großteil des CSS-Moduls entfällt dann. Alternativ font: inherit setzen und text-decoration: none in .button:hover aufnehmen.
| return ( | ||
| <section className={styles.relatedArticles} data-pagefind-ignore> | ||
| <h2 className={styles.heading}>Related Articles</h2> | ||
| <BlogPostGrid items={items} stretchFewItems titleAs="h3" /> |
There was a problem hiding this comment.
Das Grid nutzt repeat(auto-fill, minmax(300px, 1fr)) bzw. 2 Spalten zwischen 769 und 1200 px. Die Hauptspalte (col--9) ist auf dem Desktop nur etwa 800–934 px breit, also zu schmal für 3 × 300 px plus Abstände. Die dritte Karte steht dann allein in der zweiten Zeile. stretchFewItems behandelt nur 1 oder 2 Items. → Fix: Entweder MAX_RELATED_POSTS = 2 setzen (passt zum vorhandenen --pair-Layout) oder für die Related-Section ab 769 px ein eigenes repeat(3, 1fr) vorgeben. Das ist eine Designentscheidung des Autors.
| setCopied(true); | ||
| clearTimeout(copiedTimeout.current); | ||
| copiedTimeout.current = setTimeout(() => setCopied(false), 2000); | ||
| } catch { |
There was a problem hiding this comment.
In einem unsicheren Kontext (http-Preview) oder bei verweigerter Berechtigung passiert beim Klick sichtbar nichts. Auch „Copied!“ wird ohne aria-live nicht vorgelesen. → Fix: Ein Element mit aria-live="polite" ergänzen und im Fehlerfall z. B. „Copy failed“ anzeigen.
| async function handleNativeShare() { | ||
| try { | ||
| await navigator.share({ title, url }); | ||
| } catch { |
There was a problem hiding this comment.
navigator.share verschluckt alle Fehler — ShareButtons/index.tsx:35 Nur AbortError (Abbruch durch den Nutzer) sollte ignoriert werden. NotAllowedError, DataError usw. gehen sonst verloren.
| <a className={styles.button} href={linkedInHref} target="_blank" rel="noopener noreferrer"> | ||
| </a> | ||
| <a className={styles.button} href={xHref} target="_blank" rel="noopener noreferrer"> |
There was a problem hiding this comment.
Share-Links ohne sprechenden Namen — ShareButtons/index.tsx:56 Ein Screenreader liest nur „Link, X“ vor. Es fehlt der Hinweis, dass der Link teilt und einen neuen Tab öffnet. → Fix: z. B. aria-label="Share on X (opens in new tab)".
| @@ -15,6 +17,8 @@ export default function BlogPostItemWrapper(props: Props): React.JSX.Element { | |||
| <> | |||
| <img src={assets.image}></img> | |||
| readonly relatedItems?: RelatedItems; | ||
| }; | ||
|
|
||
| export default function BlogPostPageWrapper({ relatedItems = [], ...props }: Props): ReactNode { |
There was a problem hiding this comment.
Das Plugin könnte relatedItems in route.context legen, und RelatedArticles liest es mit useRouteContext(). So macht es das Blog-Plugin bereits für blogMetadata. Damit entfielen [BlogPostPage/index.tsx] und [RelatedArticles/context.tsx]. Vor dem Umbau zu prüfen: ob __import-Arrays im Route-Context genauso aufgelöst werden wie in modules.
| import type { Content } from '@theme/BlogPostPage'; | ||
|
|
||
| /** Same shape as the `items` passed to the blog list, tag and author pages. */ | ||
| export type RelatedItems = readonly { readonly content: Content }[]; |
There was a problem hiding this comment.
RelatedItems kopiert den Typ der items-Prop von BlogPostGrid. Besser den Typ aus BlogPostGrid exportieren und wiederverwenden.
| * @param {BlogPost} candidate | ||
| */ | ||
| function computeScore(post, candidate) { | ||
| const tags = new Set(getTagPermalinks(post)); |
There was a problem hiding this comment.
Die Tag- und Autoren-Sets des aktuellen Posts werden für jeden Kandidaten neu gebaut (O(N²) Allokationen). Bei der Größe des Blogs ist das irrelevant. Sauberer wäre es, die Sets einmal in getRelatedPosts zu bauen.
| "write-heading-ids": "docusaurus write-heading-ids", | ||
| "typecheck": "tsc" | ||
| "typecheck": "tsc", | ||
| "test": "node --test plugins" |
There was a problem hiding this comment.
node --test plugins übergibt ein Verzeichnis. Nur Node 20 durchsucht ein solches Argument rekursiv. Ab Node 21 gilt das Argument als Glob-Muster und trifft nur den Ordner selbst. Node versucht dann, plugins als Modul zu laden, und meldet „Cannot find module“. Die Tests laufen gar nicht erst. engines erlaubt >=20.0, und Node 20 ist seit April 2026 EOL. Lokal nicht reproduziert, weil hier nur Node 20 installiert ist. → Fix: "test": "node --test "plugins/**/*.test.js"". Gleichzeitig engines auf >=22 und die CI-Workflows auf node-version: 22 heben, denn Globs werden erst ab Node 21 unterstützt.
Adds a “Related Articles” section to blog posts, showing up to three recommendations based on shared tags and authors. Introduces sharing options for LinkedIn, X, copying links, and native sharing where supported. Reuses the existing article grid and adds unit tests for recommendation ranking and filtering.