=============== LIBRARY RULES =============== From library maintainers: - BIThesis 文档部署于 bithesis.bitnp.net,主要内容位于该域名下的 /guide/ 和 /faq/ - 这是一个文档网站,用户不关心文档是怎么构建的,只关心文档里写了什么 - 如果 /faq/ 中有解答,可引导用户直接访问网页链接 - 如果用户用中文提问,尽量用中文回答 # BIThesis Wiki Documentation BIThesis Wiki is a comprehensive documentation website for BIThesis, an unofficial LaTeX template collection for Beijing Institute of Technology. The project serves undergraduate and graduate students by providing thesis templates, formatting guides, and troubleshooting resources. Built with VitePress, this static documentation site offers searchable guides, FAQ sections, and video tutorials to help students write academic papers using LaTeX instead of Word. The project has received official recognition and support from BIT's Graduate School, Academic Affairs Office, and School of Computer Science. The documentation covers the complete workflow from installing LaTeX distributions (TeX Live/MacTeX) to configuring editors (VS Code, TeXstudio) and compiling documents. It includes detailed solutions to common LaTeX problems, bibliography management with BibTeX, and conversion utilities. The site features Chinese word segmentation for search, custom link rendering for package documentation references, and automatic generation of FAQ navigation. The project emphasizes ease of use while maintaining professional typesetting standards required by university guidelines. Advanced features include Naive UI integration with SSR support, automated sitemap page generation for AI knowledge bases, intelligent prev/next navigation links across FAQ pages, interactive configuration generators for template options, contributor showcase components, and release notes management system. ## Development Setup ```bash # Clone repository git clone https://github.com/BITNP/BIThesis-wiki cd BIThesis-wiki # Install dependencies using pnpm pnpm install # Start development server with hot reload pnpm dev # Build static site for production pnpm build # Preview production build locally pnpm preview ``` ## Project Structure ```bash # Main documentation content wiki/ ├── .vitepress/ │ ├── config.mts # VitePress configuration │ ├── util.ts # Title extraction utilities │ ├── sitemap_page.ts # Sitemap HTML generator │ ├── theme/ │ │ ├── index.ts # Theme setup │ │ ├── Layout.vue # Custom layout with tag display │ │ ├── FAQList.vue # FAQ list with tag filtering │ │ ├── BITSetupExample.vue # Interactive template config generator │ │ ├── PubAuthorAnnotation.vue # Author annotation helper │ │ ├── Contribution.vue # Contributors grid display │ │ ├── faq.data.ts # FAQ data loader (runtime) │ │ ├── faq_data.ts # FAQ data loader (build-time) │ │ ├── contributors.data.ts # Contributors loader (runtime) │ │ ├── contributors_data.ts # Contributors data (shared) │ │ ├── news.data.ts # News/release notes loader │ │ ├── link_render.ts # Custom markdown link renderer │ │ ├── util.ts # Tag normalization utilities │ │ └── custom.css # Custom styles │ └── naive-ui-adapter/ # Naive UI SSR integration │ ├── config.ts # Vite SSR config │ └── theme.ts # Theme adapter with dark mode sync ├── guide/ # User guides ├── faq/ # FAQ articles ├── news/ # Version release notes └── video/ # Video tutorial pages # Configuration files package.json # Project dependencies and scripts .prettierrc # Code formatting rules ``` ## VitePress Configuration ```typescript // wiki/.vitepress/config.mts import assert from 'node:assert' import * as footnote from 'markdown-it-footnote' import { defineConfig, type DefaultTheme } from 'vitepress' import LinkRender from './theme/link_render' import { generate_sitemap_page } from './sitemap_page' import { vite_ssr, postRender, transformHtml } from './naive-ui-adapter/config' import { generate_index_tex, generate_prev_next_links } from './theme/faq_data' export default defineConfig({ lang: 'zh-CN', title: 'BIThesis', description: '北京理工大学非官方 LaTeX 模板集合', // Enable last updated timestamps lastUpdated: true, // Sitemap generation sitemap: { hostname: 'https://bithesis.bitnp.net', }, // Head tags for favicons and meta tags head: [ ['link', { rel: 'icon', type: 'image/png', sizes: '32x32', href: '/favicon-32x32.png' }], ['link', { rel: 'icon', type: 'image/png', sizes: '16x16', href: '/favicon-16x16.png' }], ['link', { rel: 'apple-touch-icon', sizes: '180x180', href: '/apple-touch-icon.png' }], ['link', { rel: 'manifest', href: '/site.webmanifest' }], ['meta', { property: 'og:image', itemprop: 'image primaryImageOfPage', content: '/bithesis.png' }], ], // Theme configuration with navigation and sidebar themeConfig: { logo: '/apple-touch-icon.png', externalLinkIcon: true, // Edit link configuration editLink: { pattern: 'https://github.com/BITNP/BIThesis-wiki/edit/main/wiki/:path', text: '帮助我们改善此页面!', }, nav: [ { text: '文档指南', link: '/guide/preface' }, { text: '疑难杂症', link: '/faq/' }, { text: 'Overleaf', link: '/guide/preface#q-bithesis-都包含哪些模板' }, { text: '模板下载', link: '/guide/downloading-using-templates' }, { text: '加入讨论', items: [ { text: 'QQ 群:737548118', link: 'https://jq.qq.com/?_wv=1027&k=KYDrmS5z' }, { text: 'Contributing', link: 'https://github.com/BITNP/BIThesis/blob/master/contributing.md' } ] }, { text: 'GitHub', link: 'https://github.com/BITNP/BIThesis' } ], // Sidebar organized by section sidebar: { '/guide': [ { text: '前言', items: [{ text: '写在开头', link: '/guide/preface' }] }, { text: '食用方法', items: [ { text: '简介', link: '/guide/intro' }, { text: '如何开始', link: '/guide/getting-started' }, { text: '下载模板', link: '/guide/downloading-using-templates' }, { text: '编辑器配置与模板编译', link: '/guide/configure-and-compile' } ] }, { text: '常见问题', items: [ { text: '询问人工智能', link: '/guide/ask-computer' }, { text: '将 LaTeX 转换为 Word', link: '/guide/converting-to-word' }, { text: 'LaTeX 学习与使用资源', link: '/guide/resources' }, { text: '常用命令', link: '/guide/commands' }, { text: '疑难杂症', link: '/faq/' } ] }, { text: '致谢', items: [{ text: 'Thanks', link: '/guide/acknowledgements' }] } ], '/news': [ { text: '更新说明', items: [ { link: '/news/', text: '目录' }, { link: '/news/2026', text: '2025年11月至 +∞' }, { link: '/news/2025', text: '2024年11月至2025年10月' }, { link: '/news/2024', text: '2023年11月至2024年10月' }, { link: '/news/2023', text: '2022年11月至2023年10月' }, { link: '/news/2022', text: '2021年11月至2022年10月' }, { link: '/news/2021', text: '2020年11月至2021年10月' }, { link: '/news/2020', text: '−∞ 至2020年10月' } ] } ], '/video': [ { text: '视频介绍', items: [ { text: '前言', link: '/video/intro' }, { text: '第一节 综述', link: '/video/episode-1' }, { text: '第二节 LaTeX 的下载和安装', link: '/video/episode-2' }, { text: '第三节 LaTeX 基本介绍', link: '/video/episode-3' }, { text: '第四节 模板的下载和使用', link: '/video/episode-4' }, { text: '第五节 格式转化', link: '/video/episode-5' }, { text: '第六节 项目介绍和疑难解惑', link: '/video/episode-6' } ] } ] }, // Outline configuration outline: { level: [2, 4], label: '本页目录', }, // Footer configuration footer: { message: 'Released under the LaTeX Project Public License.', copyright: 'Copyright © 2020–2025 BITNP', }, // Chinese word segmentation for search search: { provider: 'local', options: { miniSearch: { options: { processTerm: (term) => { const segmenter = new Intl.Segmenter('zh', { granularity: 'word' }) if (!segmenter) return term return Array.from(segmenter.segment(term)).map(({ segment }) => segment) } } } } } }, // Markdown extensions markdown: { config: (md) => { md.use(footnote.default ?? footnote).use(LinkRender) }, anchor: { getTokensText: (tokens) => tokens // Add `html_inline` from `LinkRender` .filter((t) => ['text', 'code_inline', 'html_inline'].includes(t.type)) .map((t) => t.meta?.slug ?? t.content) .join('') } }, // Page redirection via HTTP-REFRESH meta tag transformHead({ pageData: { frontmatter } }) { if (frontmatter['redirect-to']) { const to = frontmatter['redirect-to'] return [ ['link', { rel: 'canonical', href: to }], ['meta', { 'http-equiv': 'refresh', content: `0; url=${to}` }], ['meta', { name: 'robots', content: 'noindex' }], ['script', {}, `window.location = "${to}"`] ] } }, // Transform page data to add prev/next navigation transformPageData(page, context) { // Add pre/next links to `/faq/*` if (page.relativePath === 'faq/index.md') { const sidebar = context.siteConfig.userConfig.themeConfig.sidebar['/guide'] as DefaultTheme.SidebarItem[] assert.ok(sidebar) const section = sidebar.at(-2) assert.strictEqual(section?.text, '常见问题') assert.strictEqual(section.items?.at(-1)?.link, '/faq/') page.frontmatter.prev ??= section.items.at(-2) page.frontmatter.next ??= sidebar.at(-1)?.items?.at(0) } else if (page.relativePath.startsWith('faq/')) { const { prev, next } = generate_prev_next_links(page) page.frontmatter.prev ??= prev page.frontmatter.next ??= next } // Set outline level for `/news/*` if (page.relativePath.startsWith('news/')) { page.frontmatter.outline = { level: 2 } } }, // Build hooks for generating index and sitemap async buildEnd(site) { await Promise.all([generate_index_tex(site), generate_sitemap_page(site)]) }, // Naive UI adapter vite: { ssr: vite_ssr }, postRender, transformHtml }) ``` ## Sitemap Page Generation ```typescript // wiki/.vitepress/sitemap_page.ts import { readFile, writeFile } from 'node:fs/promises' import path from 'node:path' import type { SiteConfig } from 'vitepress' import { getTitleFromSrc } from './util' /** * Generate sitemap page (not sitemap.xml) for AI knowledge base import. * Creates an HTML file with all site links sorted by category. */ export async function generate_sitemap_page(site: SiteConfig) { const pages = site.pages.map((page) => ({ file: path.join(site.srcDir, page), url: (site.rewrites.map[page] ?? page) .replace(/(^|\/)index\.md$/, '$1') .replace(/\.md$/, site.cleanUrls ? '' : '.html') })) const links = await Promise.all( pages.map(async ({ file, url }) => { const src = await readFile(file, 'utf-8') const title = getTitleFromSrc(src) ?? (url || '首页') return { url, title } }) ) // Sort by category: homepage first, then guide/, then others alphabetically links.sort( (a, b) => by_category(a, b, (x) => x.url === '') ?? by_category(a, b, (x) => x.url.startsWith('guide/')) ?? a.url.localeCompare(b.url) ) await writeFile( path.join(site.outDir, 'sitemap.html'), [ '' ].join('\n') ) } function by_category(a: Item, b: Item, is_superior: (x: Item) => boolean): number | undefined { const a_superior = is_superior(a) const b_superior = is_superior(b) if (a_superior !== b_superior) { return a_superior ? -1 : +1 } } ``` ## Title Extraction Utility ```typescript // wiki/.vitepress/util.ts import matter from 'gray-matter' /** * Extract title from markdown source by finding the first H1 heading. * Removes frontmatter, hyperlinks, and markdown formatting from the title. */ export function getTitleFromSrc(src: string): string | undefined { const lines = matter(src).content.split('\n') for (const line of lines) { if (/^# /.test(line)) { let title = line.replace(/^# /, '') // Remove hyperlink if exists if (/\[(.*)]\(.*\)/.test(title)) { const execValue = /(.*)?\[(.*)]\((.*)\)(.*)?/.exec(title) || '' title = execValue.length > 0 ? `${execValue[1] || ''}${execValue[2] || ''}${execValue[4] || ''}` : '' } // Remove markdown formatting: bold, italic, strikethrough, code title = title .replace(/\*{1,2}([^*]+?)\*{1,2}/g, '$1') .replace(/_{1,2}([^_]+?)_{1,2}/g, '$1') .replace(/~{1,2}([^~]+?)~{1,2}/g, '$1') .replace(/`{1,3}([^`]+?)`{1,3}/g, '"$1"') return title } } } ``` ## Custom Link Rendering for Package References ```typescript // wiki/.vitepress/theme/link_render.ts // Converts [[pkg:package-name]] and [[texdoc:doc-name]] to external links import type MarkdownIt from 'markdown-it' export default function LinkRender(md: MarkdownIt) { md.inline.ruler.before('link', 'custom_link', (state, silent) => { // Match [[pkg:package]] or [[texdoc:document|display text]] const match = state.src.slice(state.pos).match(/^\[\[(pkg|texdoc):([^\]|]+)(?:\|([^\]]+))?\]\]/) if (!match) return false const [fullMatch, type, target, displayText] = match const text = displayText || target if (!silent) { const token = state.push('html_inline', '', 0) if (type === 'pkg') { token.content = `${text}` } else if (type === 'texdoc') { token.content = `${text}` } } state.pos += fullMatch.length return true }) } ``` ## FAQ Data Loading and Navigation ```typescript // wiki/.vitepress/theme/faq_data.ts (build-time) import { writeFileSync } from 'node:fs' import path from 'node:path' import { createContentLoader, type PageData, type SiteConfig } from 'vitepress' import { normalizeTag, tagURL } from './util' import { getTitleFromSrc } from '../util' export interface FAQItem { title: string url: string tag: string[] } /** * Factory to create a content loader for FAQ pages. * The loader extracts titles and tags from frontmatter and sorts by URL. */ export const factory = () => createContentLoader('faq/*.md', { includeSrc: true, transform(raw): FAQItem[] { return raw .filter(({ url }) => url !== '/faq/') // Ignore `index.md` .map(({ url, frontmatter, src }) => { const title = getTitleFromSrc(src as string) if (!title) { console.warn(`未能从 ${url} 中提取 title,将用 URL 替代`) } const tag = normalizeTag(frontmatter.tag) if (tag.length === 0) { console.warn(`${url}(${title})应当在 frontmatter 中标注至少一个 tag,但目前缺失`) } return { title: title ?? url, url, tag } }) .sort((a, b) => a.url.localeCompare(b.url)) } }) /** * Generate `faq/index.tex` for the development of `bithesis.pdf`. * Creates a LaTeX itemize list with hyperlinks to all FAQ pages. */ export async function generate_index_tex(site: SiteConfig) { const pages = await factory().load() const index_tex = [ '\\begin{itemize}', ...pages.map(({ title, url }) => ' \\item ' + as_latex_href(title, `https://bithesis.bitnp.net${url}`)), '\\end{itemize}' ].join('\n') writeFileSync(path.join(site.outDir, 'faq/index.tex'), index_tex) } /** * Format as a LaTeX \href with proper escaping of special characters. */ function as_latex_href(title: string, url: string): string { const escaped = str_esc(title, [ ['\\\\', '\\textbackslash{}'], ['(?', '\\textgreater{}'], ['\\^', '\\textasciicircum{}'] ]) return `\\href{${url}}{${escaped}}` } function str_esc(s: string, m: [string, string][]): string { return m.reduce((acc, [re, repl]) => { return acc.replace(new RegExp(`${re}`, 'g'), repl) }, s) } /** * Generate prev/next navigation links for FAQ pages. * Prev always points to /faq/, next points to tag-filtered list if tag exists. */ export function generate_prev_next_links(page: PageData): { prev: { text: string; link: string } next?: { text: string; link: string } } { const prev = { text: '疑难解答', link: '/faq/' } const tag = normalizeTag(page.frontmatter.tag).at(0) const next = tag ? { text: `${tag} 问题目录`, link: tagURL(tag) } : undefined return { prev, next } } ``` ```typescript // wiki/.vitepress/theme/faq.data.ts (runtime) import { factory, type FAQItem } from './faq_data' declare const data: FAQItem[] export { data } export default factory() ``` ## FAQ List Component with Tag Filtering ```vue ``` ## Interactive BITSetup Configuration Generator ```vue ``` ## Publication Author Annotation Helper ```vue ``` ## Contributors Data and Display Component ```typescript // wiki/.vitepress/theme/contributors_data.ts export interface Contributor { /** Name, must be unique */ name: string /** URL to the avatar */ avatar: string /** URL of the homepage, e.g. GitHub user page. */ homepage?: string /** * An alternative text description of the avatar * Default: `name` */ avatar_alt?: string } export const contributors: Contributor[] = [ { name: '子衿', homepage: 'https://github.com/ZIJIN-Evan', avatar: 'https://i.loli.net/2020/04/22/1REvcJuP4iLYfQp.jpg', }, { name: 'Spencer Woo', homepage: 'https://github.com/BITNP', avatar: 'https://i.loli.net/2020/03/10/KqToYeg1buLGwsh.png', }, // ... more contributors ] /** Get a `Contributor` by his/her `name` */ export function get_contributor(name: string): Contributor { const one = contributors.find((c) => c.name === name) if (one === undefined) { throw new Error(`Failed to find a contributor with name "${name}".`) } return one } ``` ```vue ``` ## News Data Loader ```typescript // wiki/.vitepress/theme/news.data.ts import { createContentLoader } from 'vitepress' export interface NewsPost { /** @example `"−∞ 至2020年10月"` */ period: string /** @example `"/news/2020"` */ url: string /** * 按时间倒序排列。 * @example `["[1.2.0-beta-3] - 2020-06-01", "[1.1.0] - 2020-05-19"] */ releases: string[] } declare const data: NewsPost[] export { data } export default createContentLoader('news/*.md', { includeSrc: true, transform(raw): NewsPost[] { return raw .filter(({ url }) => url !== '/news/') // Ignore `index.md` .map(({ url, src }) => parse(url, src as string)) .sort((a, b) => b.url.localeCompare(a.url)) // 按时间倒序排列 }, }) function parse(url: string, src: string): NewsPost { const lines = src.split('\n') const period = lines.find((l) => /^# 更新说明:/.test(l))?.replace('# 更新说明:', '') if (!period) { console.warn(`未能从 ${url} 中提取一级标题,将用 URL 替代`) } const releases = lines .filter((l) => /^## /.test(l)) ?.map((line) => { const release = /^## (.+?)(?:\{.+\})?$/.exec(line.trim())?.at(1)?.trim() if (!release) { console.warn(`未能从 ${url} 中提取二级标题,将跳过:"${line}"`) } return release }) .filter((r) => r !== undefined) return { period: period ?? url, url, releases } } ``` ## Utility Functions ```typescript // wiki/.vitepress/theme/util.ts /** * Normalize tag from frontmatter (can be string, array, or undefined). */ export function normalizeTag(tag: string[] | string | undefined): string[] { return typeof tag === 'string' ? [tag] : (tag ?? []) } /** * Generate URL for tag-filtered FAQ list. * If tag is null, returns URL for all FAQs. */ export function tagURL(tag: string | null): string { return '/faq/' + (tag ? `?tag=${tag}` : '') + '#faq-list' } /** * Generate anchor link for news post release section. */ export function with_anchor(news_post_url: string, release: string): string { return news_post_url + '#_' + release.replaceAll(/[\s\[\]]/g, '').replaceAll(/[.]/g, '-') } ``` ## Custom Layout with Tag Display ```vue ``` ## Naive UI SSR Integration ```typescript // wiki/.vitepress/naive-ui-adapter/config.ts import type { SSGContext, Awaitable } from 'vitepress' const fileAndStyles: Record = {} export const vite_ssr = { noExternal: ['naive-ui', 'date-fns', 'vueuc'] } /** * Extract and store Naive UI styles during SSR rendering. * Removes style tags from content to be reinserted in transformHtml. */ export function postRender(context: SSGContext): Awaitable { const styleRegex = /((.|\s)+)<\/css-render-style>/ const vitepressPathRegex = /(.+)<\/vitepress-path>/ const style = styleRegex.exec(context.content)?.[1] const vitepressPath = vitepressPathRegex.exec(context.content)?.[1] if (vitepressPath && style) { fileAndStyles[vitepressPath] = style } context.content = context.content.replace(styleRegex, '') context.content = context.content.replace(vitepressPathRegex, '') } /** * Insert stored Naive UI styles into the HTML head. */ export function transformHtml(code: string, id: string): Awaitable { const html = id.split('/').pop() if (!html) return const style = fileAndStyles[`/${html}`] if (style) { return code.replace(/<\/head>/, `${style}`) } } ``` ```typescript // wiki/.vitepress/naive-ui-adapter/theme.ts import { setup } from '@css-render/vue3-ssr' import { darkTheme, lightTheme, NConfigProvider } from 'naive-ui' import { useData, useRoute } from 'vitepress' import { defineComponent, reactive, inject, h, watchEffect } from 'vue' import Layout from '../theme/Layout.vue' const CssRenderStyle = defineComponent({ setup() { const collect = inject('css-render-collect') as () => any return { style: collect() } }, render() { return h('css-render-style', { innerHTML: this.style }) }, }) const VitepressPath = defineComponent({ setup() { const route = useRoute() return () => h('vitepress-path', null, [route.path]) }, }) const NaiveUIProvider = defineComponent({ setup() { const isDark = useData().isDark const providerProps = reactive({ abstract: true, inlineThemeDisabled: true, theme: isDark ? darkTheme : lightTheme, }) return { isDark, providerProps } }, mounted() { watchEffect(() => { this.providerProps.theme = this.isDark ? darkTheme : lightTheme }) }, render() { return h(NConfigProvider, this.providerProps, { default: () => [ h(Layout, null, { default: this.$slots.default?.() }), import.meta.env.SSR ? [h(CssRenderStyle), h(VitepressPath)] : null, ], }) }, }) export { NaiveUIProvider as Layout } export function enhanceApp({ app }) { if (import.meta.env.SSR) { const { collect } = setup(app) app.provide('css-render-collect', collect) } } ``` ## Theme Configuration ```typescript // wiki/.vitepress/theme/index.ts import { Theme } from 'vitepress' import DefaultTheme from 'vitepress/theme' import { Layout, enhanceApp } from '../naive-ui-adapter/theme' import './custom.css' export default { extends: DefaultTheme, Layout, enhanceApp } satisfies Theme ``` ## LaTeX Compilation Commands ```bash # Method 1: Using latexmk (recommended - handles dependencies automatically) latexmk # Method 2: Manual compilation with xelatex and biber xelatex -no-pdf --interaction=nonstopmode main biber main xelatex -no-pdf --interaction=nonstopmode main xelatex --interaction=nonstopmode main # Clear auxiliary files and cache latexmk -c # Count words in LaTeX document texcount main.tex -merge -chinese -stat -total # Count words by chapter texcount main.tex -merge -sub=chapter # Search for package documentation texdoc biblatex-gb7714-2015 # Install missing TeX packages tlmgr install minted # Update all TeX packages tlmgr update --all # Search for files in TeX distribution tlmgr search --global --file 'times.sty' ``` ## VS Code LaTeX Workshop Configuration ```json // .vscode/settings.json { // Set default recipe to xelatex for Chinese documents "latex-workshop.latex.recipe.default": "latexmk (xelatex)", // Define recipes (compilation workflows) "latex-workshop.latex.recipes": [ { "name": "latexmk (xelatex)", "tools": ["xelatexmk"] } ], // Define tools (individual commands) "latex-workshop.latex.tools": [ { "name": "xelatexmk", "command": "latexmk", "args": [ "-synctex=1", "-interaction=nonstopmode", "-file-line-error", "-xelatex", "-outdir=%OUTDIR%", "%DOC%" ] } ], // Disable auto-build to avoid frequent disk writes "latex-workshop.latex.autoBuild.run": "never" } ``` ## TeX Live Minimal Installation ```bash # Linux: Install base infrastructure only curl -LO https://mirror.ctan.org/systems/texlive/tlnet/install-tl-unx.tar.gz tar -zxvf install-tl-unx.tar.gz cd install-tl-* mkdir ~/.texlive/ ./install-tl --texdir=~/.texlive --scheme=scheme-minimal --no-doc-install --no-src-install # Add to PATH (add to ~/.bashrc or ~/.zshrc) export PATH="$HOME/.texlive/bin/x86_64-linux:$PATH" # Install BIThesis required packages curl -LO https://github.com/BITNP/BIThesis/raw/refs/heads/main/.github/tl_packages tlmgr update --self tlmgr install $(sed -E 's/#.*//' ./tl_packages) # Windows: Install with PowerShell curl -LO https://github.com/BITNP/BIThesis/raw/refs/heads/main/.github/tl_packages tlmgr install ((Get-Content ./tl_packages) -replace '\s*#.*', '') ``` ## Page Redirection Configuration ```typescript // Frontmatter in markdown file --- redirect-to: /guide/new-location --- // Handled in config.mts transformHead hook transformHead({ pageData: { frontmatter } }) { if (frontmatter['redirect-to']) { const to = frontmatter['redirect-to'] return [ ['link', { rel: 'canonical', href: to }], ['meta', { 'http-equiv': 'refresh', content: `0; url=${to}` }], ['meta', { name: 'robots', content: 'noindex' }], ['script', {}, `window.location = "${to}"`] ] } } ``` ## Contributing New FAQ Pages ```markdown --- tag: - bithesis # BIThesis-specific issue - package # Requires external package --- # Problem Title Description of the problem and solution. ```latex \documentclass[twoside=false]{bithesis} \begin{document} Example solution code here. \end{document} ``` Link to related resources: [[pkg:algorithm2e]] and [[texdoc:biblatex]]. ``` ## Code Formatting and Linting ```bash # Format all files with Prettier pnpm format # Check formatting without modifying files pnpm lint:prettier # Prettier configuration (.prettierrc) { "semi": false, "singleQuote": true, "printWidth": 120, "trailingComma": "es5" } ``` ## Deployment Configuration ```yaml # Example: Deploy to Vercel or Netlify # Build command: pnpm build # Output directory: wiki/.vitepress/dist # Node version: 22.x # Vercel configuration (vercel.json) { "buildCommand": "pnpm build", "outputDirectory": "wiki/.vitepress/dist", "framework": "vitepress" } # Netlify configuration (netlify.toml) [build] command = "pnpm build" publish = "wiki/.vitepress/dist" ``` ## Summary BIThesis Wiki demonstrates effective technical documentation practices for academic LaTeX templates. The site combines VitePress's static site generation with custom markdown extensions for package references, enabling seamless navigation between documentation and external resources. The FAQ system with tag filtering helps users quickly find solutions to common LaTeX problems, while the comprehensive guides reduce the barrier to entry for students new to LaTeX. Advanced features include automated sitemap HTML generation for AI knowledge bases, intelligent prev/next navigation links, sophisticated title extraction from markdown sources, and interactive configuration generators with shareable links for template options. Integration patterns include Chinese text search through Intl.Segmenter, Naive UI components with full SSR support and synchronized dark mode for enhanced interactivity, automated build-time generation of LaTeX index files, and a robust contributors showcase system. The project emphasizes minimal configuration while providing complete control over compilation workflows across different editors. The dual FAQ data loader architecture (runtime and build-time) optimizes both user experience and build performance. Interactive components like BITSetupExample.vue and PubAuthorAnnotation.vue provide user-friendly interfaces for generating LaTeX configuration code with real-time previews and shareable anchor links. Deployment is straightforward through static hosting platforms, with continuous integration ensuring documentation stays synchronized with template releases. The modular architecture supports easy contribution through markdown files, making it simple for the community to expand the knowledge base with comprehensive video tutorials covering all aspects of LaTeX usage.