From 53ee795102e7f57fc3f2f646defecfd9f038fcfd Mon Sep 17 00:00:00 2001 From: Filip Date: Fri, 9 Oct 2026 16:26:14 +0200 Subject: [PATCH 1/4] Enable h1-h6 anchor links and TOC depth Co-Authored-By: Claude Sonnet 5.5 --- nuxt.config.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/nuxt.config.ts b/nuxt.config.ts index 7c320773..c1ad6dc8 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -22,15 +22,22 @@ export default defineNuxtConfig({ }, content: { renderer: { - anchorLinks: false + // Make every heading (h1-h6) a clickable #anchor link + anchorLinks: { h1: true, h2: true, h3: true, h4: true, h5: true, h6: true } }, highlight: { theme: { default: 'github-dark', }, }, - markdown: { - anchorLinks: false + build: { + markdown: { + // Include h1-h6 in the generated table of contents (default is h2-h3) + toc: { + depth: 6, + searchDepth: 6, + }, + }, }, api: { baseURL: '/site-api/_content' From f74a9b24d9c68209cff92b1852aaeb908fae3050 Mon Sep 17 00:00:00 2001 From: Filip Date: Fri, 9 Oct 2026 16:26:14 +0200 Subject: [PATCH 2/4] Render nested h1-h6 entries in table of contents Co-Authored-By: Claude Sonnet 5.5 --- components/app/toc.vue | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/components/app/toc.vue b/components/app/toc.vue index 93ab6268..9ba1f210 100644 --- a/components/app/toc.vue +++ b/components/app/toc.vue @@ -7,23 +7,14 @@
    -
  • +
  • {{ link.text }} -
@@ -54,6 +45,19 @@ const props = withDefaults(defineProps<{ const { ui } = useUI("toc", toRef(props, "ui"), config); +// Flatten the nested toc (h1-h6) into a list, keeping each link's nesting depth for indentation +const flatLinks = computed(() => { + const result: { id: string, text: string, depth: number }[] = []; + const walk = (links: any[] = [], depth = 0) => { + for (const link of links) { + result.push({ id: link.id, text: link.text, depth }); + walk(link.children, depth + 1); + } + }; + walk(props.page?.body?.toc?.links); + return result; +}); + const activeSection = ref(null); const isActive = (id: string) => { From 207968f1cfeed67a146599e06eeaa8109ee7b38e Mon Sep 17 00:00:00 2001 From: Filip Date: Fri, 9 Oct 2026 16:26:14 +0200 Subject: [PATCH 3/4] Add heading hover hash effect and dynamic scroll offset Co-Authored-By: Claude Sonnet 5.5 --- app.vue | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 2 deletions(-) diff --git a/app.vue b/app.vue index 9068478d..c4032504 100644 --- a/app.vue +++ b/app.vue @@ -66,7 +66,7 @@ onMounted(() => { }); // Track all sections that have an `id` applied - document.querySelectorAll('h2, h3').forEach((section) => { + document.querySelectorAll('h1, h2, h3, h4, h5, h6').forEach((section) => { observer.observe(section); }); @@ -117,6 +117,32 @@ onBeforeUnmount(() => { window.removeEventListener('resize', handleResize); }); +// Keep scroll-padding-top equal to the sticky header's real height (it includes the +// breadcrumbs when they are visible, and changes across breakpoints), plus a small gap +let headerObserver: ResizeObserver | null = null + +const updateScrollOffset = () => { + const header = document.querySelector('header') + const offset = header ? header.getBoundingClientRect().height : 0 + document.documentElement.style.setProperty('--scroll-offset', `${Math.ceil(offset)}px`) +} + +const observeHeader = () => { + headerObserver?.disconnect() + const header = document.querySelector('header') + if (header) headerObserver?.observe(header) + updateScrollOffset() +} + +onMounted(() => { + headerObserver = new ResizeObserver(updateScrollOffset) + observeHeader() +}) + +watch(() => route.path, () => nextTick(observeHeader)) + +onBeforeUnmount(() => headerObserver?.disconnect()) + onMounted(() => { // Add the "Copy to Clipboard" button to all code blocks document.querySelectorAll('pre').forEach(pre => { @@ -141,7 +167,7 @@ const copyCode = (pre: HTMLElement) => {