bsc

Comprehensive codebase and cou...
Log | Files | Refs | Activity | README | LICENSE

commit 214e353c0db735f0100a1e4682848555162ec61b
parent 346bd8713f6ddafc6a5144e7f8bf70f56f310329
Author: Amit Dutta <mail@amit.is-a.dev>
Date:   Mon, 24 Aug 2026 15:49:21 +0530

Merge pull request #69 from notamitgamer/feat/related-pages-and-sidebar-tags

feat: related pages by tag + auto-populated sidebar tag list
Diffstat:
Mdocs/.vitepress/theme/components/Labels.vue | 2+-
Adocs/.vitepress/theme/components/RelatedPages.vue | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adocs/.vitepress/theme/components/SidebarTags.vue | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdocs/.vitepress/theme/index.ts | 6+++++-
4 files changed, 179 insertions(+), 2 deletions(-)

diff --git a/docs/.vitepress/theme/components/Labels.vue b/docs/.vitepress/theme/components/Labels.vue @@ -32,7 +32,7 @@ function humanize(tag) { display: flex; flex-wrap: wrap; gap: 10px; - margin-top: 20px; + margin: 20px 0 24px; } .bsc-tag { diff --git a/docs/.vitepress/theme/components/RelatedPages.vue b/docs/.vitepress/theme/components/RelatedPages.vue @@ -0,0 +1,85 @@ +<script setup> +import { computed } from 'vue' +import { useData, useRoute } from 'vitepress' +import { data as tagsData } from '../../tags.data.ts' + +const MAX_RELATED = 6 + +const { frontmatter } = useData() +const route = useRoute() + +function stripTitleIcon(title) { + if (!title) return title + return title.replace(/<svg[\s\S]*?<\/svg>\s*/i, '').trim() +} + +const related = computed(() => { + const tags = frontmatter.value.tags + if (!Array.isArray(tags) || tags.length === 0) return [] + + const currentPath = route.path + + const seen = new Map() + + for (const tag of tags) { + const pages = tagsData[tag] + if (!pages) continue + for (const p of pages) { + if (seen.has(p.url)) continue + if (p.url === currentPath) continue + seen.set(p.url, { title: stripTitleIcon(p.title), url: p.url, tag }) + } + } + + return Array.from(seen.values()).slice(0, MAX_RELATED) +}) +</script> + +<template> + <div v-if="related.length" class="bsc-related"> + <h3 class="bsc-related-heading">Related pages</h3> + <ul class="bsc-related-list"> + <li v-for="item in related" :key="item.url"> + <a :href="item.url">{{ item.title }}</a> + </li> + </ul> + </div> +</template> + +<style scoped> +.bsc-related { + margin-top: 24px; + margin-bottom: 24px; + padding-top: 16px; + border-top: 1px solid var(--vp-c-divider); +} + +.bsc-related-heading { + margin: 0 0 8px; + font-size: 13px; + font-weight: 600; + color: var(--vp-c-text-2); + border: none; + padding: 0; +} + +.bsc-related-list { + margin: 0; + padding-left: 0; + list-style: none; + display: flex; + flex-direction: column; + gap: 4px; +} + +.bsc-related-list a { + font-size: 14px; + color: var(--vp-c-text-1); + text-decoration: none; +} + +.bsc-related-list a:hover { + color: var(--vp-c-brand-1); + text-decoration: underline; +} +</style> diff --git a/docs/.vitepress/theme/components/SidebarTags.vue b/docs/.vitepress/theme/components/SidebarTags.vue @@ -0,0 +1,88 @@ +<script setup> +import { computed } from 'vue' +import { data as tagsData } from '../../tags.data.ts' + +const MAX_SHOWN = 12 + +function humanize(tag) { + return tag.replace(/[-_]/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase()) +} + +const topTags = computed(() => { + return Object.entries(tagsData) + .map(([tag, pages]) => ({ tag, count: pages.length })) + .sort((a, b) => b.count - a.count || a.tag.localeCompare(b.tag)) + .slice(0, MAX_SHOWN) +}) + +const hasMore = computed(() => Object.keys(tagsData).length > MAX_SHOWN) +</script> + +<template> + <div v-if="topTags.length" class="bsc-sidebar-tags"> + <p class="bsc-sidebar-tags-heading">Tags</p> + <div class="bsc-sidebar-tags-list"> + <a + v-for="t in topTags" + :key="t.tag" + class="bsc-sidebar-tag" + :href="`/tags#${t.tag}`" + >{{ humanize(t.tag) }}</a> + </div> + <a v-if="hasMore" class="bsc-sidebar-tags-more" href="/tags">View all tags →</a> + </div> +</template> + +<style scoped> +.bsc-sidebar-tags { + margin-top: 16px; + padding: 12px 0 0; + border-top: 1px solid var(--vp-c-divider); +} + +.bsc-sidebar-tags-heading { + margin: 0 0 8px; + padding: 0 24px; + font-size: 12px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.02em; + color: var(--vp-c-text-2); +} + +.bsc-sidebar-tags-list { + display: flex; + flex-wrap: wrap; + gap: 6px; + padding: 0 24px; +} + +.bsc-sidebar-tag { + font-size: 12px; + padding: 3px 8px; + color: var(--vp-c-text-2); + background: var(--vp-c-bg-soft); + border: 1px solid var(--vp-c-divider); + border-radius: 4px; + text-decoration: none; + transition: color 0.15s ease, border-color 0.15s ease; +} + +.bsc-sidebar-tag:hover { + color: var(--vp-c-brand-1); + border-color: var(--vp-c-brand-1); +} + +.bsc-sidebar-tags-more { + display: block; + margin-top: 8px; + padding: 0 24px; + font-size: 12px; + color: var(--vp-c-brand-1); + text-decoration: none; +} + +.bsc-sidebar-tags-more:hover { + text-decoration: underline; +} +</style> diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts @@ -9,6 +9,8 @@ import CopyLinkButton from './components/CopyLinkButton.vue' import MarkdownMenu from './components/MarkdownMenu.vue' import Breadcrumbs from './components/Breadcrumbs.vue' import Labels from './components/Labels.vue' +import RelatedPages from './components/RelatedPages.vue' +import SidebarTags from './components/SidebarTags.vue' import './style.css' export default { @@ -26,9 +28,11 @@ export default { ]), 'doc-footer-before': () => h(Fragment, [ h(MarkdownMenu), - h(Labels) + h(Labels), + h(RelatedPages) ]), 'doc-after': () => h(Footer), + 'sidebar-nav-after': () => h(SidebarTags), 'nav-bar-content-after': () => h(SponsorButton) }) },
© notamitgamer • Site Built: 2026-09-05 01:53:16 UTC • git-mirror commit: c170d72 [view raw info]
Originally created with stagit • modified by notamitgamer
Forked from github.com/notamitgamer/git-mirror