mirror of
https://github.com/Nanaloveyuki/BitLogger.git
synced 2026-07-24 00:42:18 +00:00
📝 add VitePress site and deployment workflow
This commit is contained in:
@@ -0,0 +1,61 @@
|
|||||||
|
name: Docs
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pages: write
|
||||||
|
id-token: write
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: docs-pages
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 22
|
||||||
|
cache: npm
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Build docs site
|
||||||
|
env:
|
||||||
|
DOCS_BASE: /
|
||||||
|
run: npm run docs:build
|
||||||
|
|
||||||
|
- name: Configure Pages
|
||||||
|
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||||
|
uses: actions/configure-pages@v5
|
||||||
|
|
||||||
|
- name: Upload Pages artifact
|
||||||
|
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||||
|
uses: actions/upload-pages-artifact@v3
|
||||||
|
with:
|
||||||
|
path: docs/.vitepress/dist
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
environment:
|
||||||
|
name: github-pages
|
||||||
|
url: ${{ steps.deployment.outputs.page_url }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Deploy to GitHub Pages
|
||||||
|
id: deployment
|
||||||
|
uses: actions/deploy-pages@v4
|
||||||
@@ -16,9 +16,15 @@ desktop.ini
|
|||||||
*.log
|
*.log
|
||||||
*.tmp
|
*.tmp
|
||||||
*.bak
|
*.bak
|
||||||
|
node_modules/
|
||||||
|
|
||||||
# Dev Documentations
|
# Dev Documentations
|
||||||
docs/dev/*
|
docs/dev/*
|
||||||
|
|
||||||
|
# Docs site build artifacts
|
||||||
|
docs/.vitepress/cache/
|
||||||
|
docs/.vitepress/dist/
|
||||||
|
docs/.vitepress/generated/
|
||||||
|
|
||||||
# vibecoding
|
# vibecoding
|
||||||
AGENTS/
|
AGENTS/
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
import { basename } from 'node:path'
|
||||||
|
import { createMoonbitLanguageRegistration } from 'moonbit-syntax-highlighter'
|
||||||
|
import { defineConfig, type DefaultTheme } from 'vitepress'
|
||||||
|
|
||||||
|
import { docsData } from './generated/docs-data.mjs'
|
||||||
|
|
||||||
|
type ApiDocEntry = {
|
||||||
|
file: string
|
||||||
|
text: string
|
||||||
|
group: string
|
||||||
|
category: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const repoSlug = process.env.GITHUB_REPOSITORY?.split('/')[1] ?? 'BitLogger'
|
||||||
|
const docsBase = process.env.DOCS_BASE ?? (process.env.GITHUB_ACTIONS ? `/${repoSlug}/` : '/')
|
||||||
|
const repository = 'https://github.com/Nanaloveyuki/BitLogger'
|
||||||
|
|
||||||
|
function splitWords(input: string): string[] {
|
||||||
|
return input
|
||||||
|
.split(/[^A-Za-z0-9]+/)
|
||||||
|
.map(part => part.trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatWord(word: string): string {
|
||||||
|
const lower = word.toLowerCase()
|
||||||
|
if (lower === 'api') return 'API'
|
||||||
|
if (lower === 'json') return 'JSON'
|
||||||
|
if (lower === 'js') return 'JS'
|
||||||
|
if (lower === 'wasm') return 'WASM'
|
||||||
|
if (lower === 'llvm') return 'LLVM'
|
||||||
|
return lower.charAt(0).toUpperCase() + lower.slice(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
function humanize(input: string): string {
|
||||||
|
const words = splitWords(input)
|
||||||
|
if (words.length === 0) return input
|
||||||
|
return words.map(formatWord).join(' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
function readApiDocs(): ApiDocEntry[] {
|
||||||
|
return docsData.api.entries.map(entry => ({
|
||||||
|
file: entry.file,
|
||||||
|
text: entry.title,
|
||||||
|
group: entry.group,
|
||||||
|
category: entry.category,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildApiSidebar(): DefaultTheme.SidebarItem[] {
|
||||||
|
const entries = readApiDocs()
|
||||||
|
const sidebar: DefaultTheme.SidebarItem[] = [{ text: 'Overview', link: '/api/' }]
|
||||||
|
const groups = new Map<string, Map<string, DefaultTheme.SidebarItem[]>>()
|
||||||
|
|
||||||
|
for (const entry of entries) {
|
||||||
|
if (entry.file === 'index.md') continue
|
||||||
|
const group = groups.get(entry.group) ?? new Map<string, DefaultTheme.SidebarItem[]>()
|
||||||
|
const categoryItems = group.get(entry.category) ?? []
|
||||||
|
categoryItems.push({
|
||||||
|
text: entry.text,
|
||||||
|
link: `/api/${basename(entry.file, '.md')}`,
|
||||||
|
})
|
||||||
|
group.set(entry.category, categoryItems)
|
||||||
|
groups.set(entry.group, group)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (groups.size === 1 && groups.has('api')) {
|
||||||
|
const categories = groups.get('api')!
|
||||||
|
for (const category of [...categories.keys()].sort((a, b) => humanize(a).localeCompare(humanize(b)))) {
|
||||||
|
sidebar.push({
|
||||||
|
text: humanize(category),
|
||||||
|
collapsed: true,
|
||||||
|
items: categories.get(category)!,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return sidebar
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const group of [...groups.keys()].sort((a, b) => humanize(a).localeCompare(humanize(b)))) {
|
||||||
|
const categories = groups.get(group)!
|
||||||
|
sidebar.push({
|
||||||
|
text: humanize(group),
|
||||||
|
collapsed: true,
|
||||||
|
items: [...categories.keys()]
|
||||||
|
.sort((a, b) => humanize(a).localeCompare(humanize(b)))
|
||||||
|
.map(category => ({
|
||||||
|
text: humanize(category),
|
||||||
|
collapsed: true,
|
||||||
|
items: categories.get(category)!,
|
||||||
|
})),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return sidebar
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildChangesSidebar(): DefaultTheme.SidebarItem[] {
|
||||||
|
return [
|
||||||
|
{ text: 'Overview', link: '/changes/' },
|
||||||
|
{
|
||||||
|
text: 'Versions',
|
||||||
|
items: docsData.changes.map(item => ({ text: item.version, link: item.link })),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
title: 'BitLogger',
|
||||||
|
description: 'Structured logging library docs for MoonBit.',
|
||||||
|
base: docsBase,
|
||||||
|
cleanUrls: true,
|
||||||
|
srcExclude: ['dev/**'],
|
||||||
|
lastUpdated: true,
|
||||||
|
markdown: {
|
||||||
|
languages: [createMoonbitLanguageRegistration()],
|
||||||
|
},
|
||||||
|
themeConfig: {
|
||||||
|
siteTitle: 'BitLogger',
|
||||||
|
nav: [
|
||||||
|
{ text: 'Home', link: '/' },
|
||||||
|
{ text: 'API', link: '/api/' },
|
||||||
|
{ text: 'Changes', link: '/changes/' },
|
||||||
|
{ text: 'Mooncake', link: 'https://mooncakes.io/docs/Nanaloveyuki/BitLogger' },
|
||||||
|
],
|
||||||
|
search: {
|
||||||
|
provider: 'local',
|
||||||
|
},
|
||||||
|
socialLinks: [{ icon: 'github', link: repository }],
|
||||||
|
editLink: {
|
||||||
|
pattern: `${repository}/edit/main/docs/:path`,
|
||||||
|
text: 'Edit this page on GitHub',
|
||||||
|
},
|
||||||
|
sidebar: {
|
||||||
|
'/api/': buildApiSidebar(),
|
||||||
|
'/changes/': buildChangesSidebar(),
|
||||||
|
},
|
||||||
|
footer: {
|
||||||
|
message: 'Published from the repository docs folder with VitePress.',
|
||||||
|
copyright: 'MIT',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -0,0 +1,219 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, shallowRef } from 'vue'
|
||||||
|
import { withBase } from 'vitepress'
|
||||||
|
|
||||||
|
import { docsData } from '../../generated/docs-data.mjs'
|
||||||
|
|
||||||
|
const selectedGroup = shallowRef('all')
|
||||||
|
const query = shallowRef('')
|
||||||
|
|
||||||
|
const groups = computed(() => docsData.api.groups)
|
||||||
|
|
||||||
|
const filteredGroups = computed(() => {
|
||||||
|
const keyword = query.value.trim().toLowerCase()
|
||||||
|
return groups.value
|
||||||
|
.filter(group => selectedGroup.value === 'all' || group.id === selectedGroup.value)
|
||||||
|
.map(group => ({
|
||||||
|
...group,
|
||||||
|
categories: group.categories
|
||||||
|
.map(category => ({
|
||||||
|
...category,
|
||||||
|
entries: category.entries.filter(entry => {
|
||||||
|
if (!keyword) return true
|
||||||
|
const haystack = [entry.title, entry.description, entry.name, ...entry.keywords]
|
||||||
|
.join(' ')
|
||||||
|
.toLowerCase()
|
||||||
|
return haystack.includes(keyword)
|
||||||
|
}),
|
||||||
|
}))
|
||||||
|
.filter(category => category.entries.length > 0),
|
||||||
|
}))
|
||||||
|
.filter(group => group.categories.length > 0)
|
||||||
|
})
|
||||||
|
|
||||||
|
const resultCount = computed(() =>
|
||||||
|
filteredGroups.value.reduce(
|
||||||
|
(total, group) => total + group.categories.reduce((sum, category) => sum + category.entries.length, 0),
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<section class="api-shell">
|
||||||
|
<div class="api-toolbar">
|
||||||
|
<label class="api-search">
|
||||||
|
<span>Find API</span>
|
||||||
|
<input v-model="query" type="search" placeholder="Search logger, sink, config, async...">
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="api-filter">
|
||||||
|
<span>Doc Group</span>
|
||||||
|
<select v-model="selectedGroup">
|
||||||
|
<option value="all">All groups</option>
|
||||||
|
<option v-for="group in groups" :key="group.id" :value="group.id">
|
||||||
|
{{ group.label }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="api-summary">
|
||||||
|
<strong>{{ resultCount }}</strong>
|
||||||
|
<span>matching API pages across {{ filteredGroups.length }} group views</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="api-groups">
|
||||||
|
<article v-for="group in filteredGroups" :key="group.id" class="api-group-card">
|
||||||
|
<header class="api-group-header">
|
||||||
|
<div>
|
||||||
|
<p class="api-group-kicker">{{ group.label }}</p>
|
||||||
|
<h3>{{ group.entryCount }} APIs in this group</h3>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="api-category-grid">
|
||||||
|
<section v-for="category in group.categories" :key="category.id" class="api-category-card">
|
||||||
|
<h4>{{ category.label }}</h4>
|
||||||
|
<p>{{ category.entries.length }} entries</p>
|
||||||
|
<ul>
|
||||||
|
<li v-for="entry in category.entries.slice(0, 8)" :key="entry.slug">
|
||||||
|
<a :href="withBase(entry.link)">{{ entry.title }}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<a v-if="category.entries.length > 8" class="api-more" :href="withBase('/api/')">
|
||||||
|
Browse {{ category.entries.length - 8 }} more in sidebar
|
||||||
|
</a>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.api-shell {
|
||||||
|
margin: 1.5rem 0 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.api-toolbar {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.9rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.api-search,
|
||||||
|
.api-filter {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.api-search span,
|
||||||
|
.api-filter span {
|
||||||
|
font-size: 0.78rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
color: #9b4d28;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.api-search input,
|
||||||
|
.api-filter select {
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 14px;
|
||||||
|
border: 1px solid var(--vp-c-divider);
|
||||||
|
background: rgba(255, 255, 255, 0.9);
|
||||||
|
padding: 0.85rem 0.95rem;
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.api-summary {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 0.65rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.api-summary strong {
|
||||||
|
font-size: 1.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.api-summary span {
|
||||||
|
color: var(--vp-c-text-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.api-groups {
|
||||||
|
display: grid;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.api-group-card {
|
||||||
|
border: 1px solid var(--vp-c-divider);
|
||||||
|
border-radius: 28px;
|
||||||
|
padding: 1rem;
|
||||||
|
background: linear-gradient(180deg, rgba(255, 251, 247, 0.96), rgba(246, 242, 236, 0.96));
|
||||||
|
}
|
||||||
|
|
||||||
|
.api-group-header h3,
|
||||||
|
.api-category-card h4,
|
||||||
|
.api-category-card p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.api-group-kicker {
|
||||||
|
margin: 0 0 0.25rem;
|
||||||
|
color: #9b4d28;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.1em;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.api-category-grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.85rem;
|
||||||
|
margin-top: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.api-category-card {
|
||||||
|
border-radius: 20px;
|
||||||
|
background: rgba(255, 255, 255, 0.88);
|
||||||
|
border: 1px solid rgba(155, 77, 40, 0.12);
|
||||||
|
padding: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.api-category-card p {
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
color: var(--vp-c-text-2);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.api-category-card ul {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0.85rem 0 0;
|
||||||
|
display: grid;
|
||||||
|
gap: 0.45rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.api-category-card a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.api-more {
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 0.8rem;
|
||||||
|
color: var(--vp-c-text-2);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 820px) {
|
||||||
|
.api-toolbar {
|
||||||
|
grid-template-columns: minmax(0, 2fr) minmax(220px, 0.8fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.api-category-grid {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,191 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import { withBase } from 'vitepress'
|
||||||
|
|
||||||
|
import { docsData } from '../../generated/docs-data.mjs'
|
||||||
|
|
||||||
|
const topCategories = computed(() => docsData.api.categories.slice(0, 6))
|
||||||
|
const recentChanges = computed(() => docsData.changes.slice(0, 4))
|
||||||
|
const apiCount = computed(() => docsData.api.entries.filter(entry => entry.slug !== 'index').length)
|
||||||
|
const groupCount = computed(() => docsData.api.groups.length)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<section class="hub-grid">
|
||||||
|
<article class="hub-panel hub-panel-accent">
|
||||||
|
<p class="hub-eyebrow">Docs Hub</p>
|
||||||
|
<h2 class="hub-title">Start from use case, not from filenames.</h2>
|
||||||
|
<p class="hub-copy">
|
||||||
|
Browse builders, runtime helpers, presets, sync logging, and async logging from one place.
|
||||||
|
</p>
|
||||||
|
<div class="hub-stats">
|
||||||
|
<div>
|
||||||
|
<strong>{{ apiCount }}</strong>
|
||||||
|
<span>API pages</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<strong>{{ groupCount }}</strong>
|
||||||
|
<span>doc groups</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<strong>{{ docsData.changes.length }}</strong>
|
||||||
|
<span>release notes</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="hub-panel">
|
||||||
|
<p class="hub-eyebrow">Popular Areas</p>
|
||||||
|
<ul class="hub-chip-list">
|
||||||
|
<li v-for="category in topCategories" :key="category.id">
|
||||||
|
<a class="hub-chip" :href="withBase('/api/')">
|
||||||
|
<span>{{ category.label }}</span>
|
||||||
|
<small>{{ category.entryCount }}</small>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="hub-panel">
|
||||||
|
<p class="hub-eyebrow">Release Trail</p>
|
||||||
|
<ul class="hub-link-list">
|
||||||
|
<li v-for="item in recentChanges" :key="item.version">
|
||||||
|
<a :href="withBase(item.link)">Version {{ item.version }}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.hub-grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 1rem;
|
||||||
|
margin: 1.5rem 0 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hub-panel {
|
||||||
|
border: 1px solid var(--vp-c-divider);
|
||||||
|
border-radius: 24px;
|
||||||
|
padding: 1.25rem;
|
||||||
|
background: linear-gradient(180deg, rgba(255, 255, 255, 0.95), rgba(247, 245, 239, 0.95));
|
||||||
|
box-shadow: 0 16px 40px rgba(62, 46, 31, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hub-panel-accent {
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at top right, rgba(191, 68, 32, 0.16), transparent 34%),
|
||||||
|
linear-gradient(180deg, rgba(255, 250, 244, 0.98), rgba(247, 241, 232, 0.98));
|
||||||
|
}
|
||||||
|
|
||||||
|
.hub-eyebrow {
|
||||||
|
margin: 0 0 0.5rem;
|
||||||
|
color: #9b4d28;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.12em;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hub-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.55rem;
|
||||||
|
line-height: 1.15;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hub-copy {
|
||||||
|
margin: 0.85rem 0 0;
|
||||||
|
color: var(--vp-c-text-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hub-stats {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
gap: 0.75rem;
|
||||||
|
margin-top: 1.15rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hub-stats strong,
|
||||||
|
.hub-stats span {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hub-stats strong {
|
||||||
|
font-size: 1.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hub-stats span {
|
||||||
|
color: var(--vp-c-text-2);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hub-chip-list,
|
||||||
|
.hub-link-list {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hub-chip-list {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||||
|
gap: 0.7rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hub-chip {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 56px;
|
||||||
|
gap: 0.8rem;
|
||||||
|
padding: 0.7rem 0.9rem;
|
||||||
|
border-radius: 999px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: inherit;
|
||||||
|
background: rgba(255, 255, 255, 0.9);
|
||||||
|
border: 1px solid rgba(155, 77, 40, 0.14);
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hub-chip span {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hub-chip small {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
color: var(--vp-c-text-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.hub-chip-list {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hub-link-list {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hub-link-list a {
|
||||||
|
color: var(--vp-c-brand-1);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 860px) {
|
||||||
|
.hub-grid {
|
||||||
|
grid-template-columns: 1.35fr 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hub-panel-accent {
|
||||||
|
grid-row: span 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.hub-stats {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
:root {
|
||||||
|
--vp-c-brand-1: #b34a24;
|
||||||
|
--vp-c-brand-2: #92381a;
|
||||||
|
--vp-c-brand-3: #d06d39;
|
||||||
|
--vp-c-brand-soft: rgba(176, 74, 36, 0.14);
|
||||||
|
--vp-home-hero-name-color: #6b2f17;
|
||||||
|
--vp-home-hero-text-color: #27170f;
|
||||||
|
--vp-home-hero-tagline-color: #5f5249;
|
||||||
|
--vp-font-family-base: "Segoe UI", "PingFang SC", "Hiragino Sans GB", sans-serif;
|
||||||
|
--vp-font-family-mono: "Cascadia Mono", "JetBrains Mono", monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPContent {
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at top right, rgba(208, 109, 57, 0.08), transparent 28%),
|
||||||
|
radial-gradient(circle at left 20%, rgba(179, 74, 36, 0.06), transparent 22%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPHomeHero .name,
|
||||||
|
.VPDoc h1,
|
||||||
|
.VPDoc h2,
|
||||||
|
.VPDoc h3 {
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPHomeHero .image-container {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc a {
|
||||||
|
text-underline-offset: 0.18em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc .custom-block,
|
||||||
|
.vp-doc div[class*='language-'] {
|
||||||
|
border-radius: 18px;
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import DefaultTheme from 'vitepress/theme'
|
||||||
|
import type { Theme } from 'vitepress'
|
||||||
|
|
||||||
|
import ApiOverview from './components/ApiOverview.vue'
|
||||||
|
import HomeDocHub from './components/HomeDocHub.vue'
|
||||||
|
|
||||||
|
import './custom.css'
|
||||||
|
|
||||||
|
const theme: Theme = {
|
||||||
|
extends: DefaultTheme,
|
||||||
|
enhanceApp({ app }) {
|
||||||
|
app.component('ApiOverview', ApiOverview)
|
||||||
|
app.component('HomeDocHub', HomeDocHub)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default theme
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
## BitLogger Update Changes
|
||||||
|
|
||||||
|
version 0.5.3
|
||||||
|
|
||||||
|
### Docs
|
||||||
|
|
||||||
|
- docs: expand `docs/api` coverage for sync and async facade aliases, builders, config models, state models, and runtime/file helper surfaces
|
||||||
|
- docs: clarify facade layering for `ConfiguredLogger`, `ApplicationLogger`, `LibraryLogger`, `ApplicationAsyncLogger`, `ApplicationTextAsyncLogger`, and the library async/text facades
|
||||||
|
- docs: refresh sync and async target-verification notes so published support claims stay aligned with the current validation matrix
|
||||||
|
|
||||||
|
### Test
|
||||||
|
|
||||||
|
- test: cover sync facade composition and parse/build contracts for configured, application, and library logger paths
|
||||||
|
- test: cover async facade helper-surface and unwrap contracts for application, library, parsed, and text-console builder paths
|
||||||
|
- test: keep sync and async package lines validated across `native`, `js`, and `wasm-gc` after the facade contract sweep
|
||||||
|
|
||||||
|
### Runtime
|
||||||
|
|
||||||
|
- fix: correct closed async backlog accounting so pending and dropped counts remain consistent after close and shutdown paths
|
||||||
|
- fix: hide internal file backend wrapper details from the public surface while preserving runtime file-helper behavior
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
- this release is a contract-locking follow-up focused on making the published API reference match the live facade behavior more directly
|
||||||
|
- facade-oriented sync and async entry paths now have stronger direct tests for alias-preserved helpers, narrowing boundaries, and unwrap recovery behavior
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
## Release Notes
|
||||||
|
|
||||||
|
Versioned BitLogger change summaries.
|
||||||
|
|
||||||
|
- [0.5.3](./0.5.3.md)
|
||||||
|
- [0.5.2](./0.5.2.md)
|
||||||
|
- [0.5.1](./0.5.1.md)
|
||||||
|
- [0.5.0](./0.5.0.md)
|
||||||
|
- [0.4.1](./0.4.1.md)
|
||||||
|
- [0.4.0](./0.4.0.md)
|
||||||
|
- [0.3.0](./0.3.0.md)
|
||||||
|
- [0.2.0](./0.2.0.md)
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
---
|
||||||
|
layout: home
|
||||||
|
|
||||||
|
hero:
|
||||||
|
name: BitLogger
|
||||||
|
text: Structured logging for MoonBit
|
||||||
|
tagline: API reference, examples, and release notes for BitLogger.
|
||||||
|
actions:
|
||||||
|
- theme: brand
|
||||||
|
text: Open API Reference
|
||||||
|
link: /api/
|
||||||
|
- theme: alt
|
||||||
|
text: Read Release Notes
|
||||||
|
link: /changes/
|
||||||
|
- theme: alt
|
||||||
|
text: Mooncake Package
|
||||||
|
link: https://mooncakes.io/docs/Nanaloveyuki/BitLogger
|
||||||
|
|
||||||
|
features:
|
||||||
|
- title: Structured logging
|
||||||
|
details: Levels, targets, message fields, and configurable text or JSON output.
|
||||||
|
- title: Sync and async paths
|
||||||
|
details: API reference covers both the main package and the async package surface.
|
||||||
|
- title: Config and runtime helpers
|
||||||
|
details: Browse builders, presets, file helpers, queue helpers, and verification notes quickly.
|
||||||
|
---
|
||||||
|
|
||||||
|
## Start Here
|
||||||
|
|
||||||
|
<HomeDocHub />
|
||||||
|
|
||||||
|
- Use [API Reference](./api/index.md) when you want the canonical one-file-per-public-API docs.
|
||||||
|
- Use [Release Notes](./changes/index.md) when you want version-scoped changes and publish-facing summaries.
|
||||||
|
- Use [Chinese README](https://github.com/Nanaloveyuki/BitLogger/blob/main/README.md) or [English README](./README-en.md) when you want a shorter project overview first.
|
||||||
Generated
+2837
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "bitlogger-docs-site",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"prepare": "node ./scripts/generate-docs-data.mjs",
|
||||||
|
"dev": "node ./scripts/generate-docs-data.mjs && node ./node_modules/vitepress/dist/node/cli.js dev docs",
|
||||||
|
"build": "node ./scripts/generate-docs-data.mjs && node ./node_modules/vitepress/dist/node/cli.js build docs",
|
||||||
|
"preview": "node ./node_modules/vitepress/dist/node/cli.js preview docs",
|
||||||
|
"docs:prepare": "node ./scripts/generate-docs-data.mjs",
|
||||||
|
"docs:dev": "node ./scripts/generate-docs-data.mjs && node ./node_modules/vitepress/dist/node/cli.js dev docs",
|
||||||
|
"docs:build": "node ./scripts/generate-docs-data.mjs && node ./node_modules/vitepress/dist/node/cli.js build docs",
|
||||||
|
"docs:preview": "node ./node_modules/vitepress/dist/node/cli.js preview docs"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"gray-matter": "^4.0.3",
|
||||||
|
"moonbit-syntax-highlighter": "^0.1.1",
|
||||||
|
"vitepress": "^1.6.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+1803
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,4 @@
|
|||||||
|
allowBuilds:
|
||||||
|
esbuild: true
|
||||||
|
minimumReleaseAgeExclude:
|
||||||
|
- moonbit-syntax-highlighter@0.1.1
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
import { mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs'
|
||||||
|
import { basename, dirname, extname, resolve } from 'node:path'
|
||||||
|
|
||||||
|
import matter from 'gray-matter'
|
||||||
|
|
||||||
|
const root = process.cwd()
|
||||||
|
const docsDir = resolve(root, 'docs')
|
||||||
|
const apiDir = resolve(docsDir, 'api')
|
||||||
|
const changesDir = resolve(docsDir, 'changes')
|
||||||
|
const outFile = resolve(docsDir, '.vitepress', 'generated', 'docs-data.mjs')
|
||||||
|
|
||||||
|
function splitWords(input) {
|
||||||
|
return input
|
||||||
|
.split(/[^A-Za-z0-9]+/)
|
||||||
|
.map(part => part.trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatWord(word) {
|
||||||
|
const lower = word.toLowerCase()
|
||||||
|
if (lower === 'api') return 'API'
|
||||||
|
if (lower === 'json') return 'JSON'
|
||||||
|
if (lower === 'js') return 'JS'
|
||||||
|
if (lower === 'wasm') return 'WASM'
|
||||||
|
if (lower === 'llvm') return 'LLVM'
|
||||||
|
return lower.charAt(0).toUpperCase() + lower.slice(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
function humanize(input) {
|
||||||
|
const words = splitWords(input)
|
||||||
|
if (words.length === 0) return input
|
||||||
|
return words.map(formatWord).join(' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractTitle(source, fallback) {
|
||||||
|
const heading = source.match(/^##\s+(.+)$/m)?.[1]?.trim()
|
||||||
|
return humanize(heading ?? fallback)
|
||||||
|
}
|
||||||
|
|
||||||
|
function readApiDocs() {
|
||||||
|
const entries = readdirSync(apiDir, { withFileTypes: true })
|
||||||
|
.filter(entry => entry.isFile())
|
||||||
|
.filter(entry => extname(entry.name) === '.md')
|
||||||
|
.filter(entry => !entry.name.startsWith('.'))
|
||||||
|
.map(entry => {
|
||||||
|
const file = entry.name
|
||||||
|
const slug = basename(file, '.md')
|
||||||
|
const source = readFileSync(resolve(apiDir, file), 'utf8')
|
||||||
|
const parsed = matter(source)
|
||||||
|
const data = parsed.data
|
||||||
|
const keywords = Array.isArray(data['key-word']) ? data['key-word'].map(String) : []
|
||||||
|
return {
|
||||||
|
file,
|
||||||
|
slug,
|
||||||
|
title: extractTitle(parsed.content, data.name ?? slug),
|
||||||
|
name: String(data.name ?? slug),
|
||||||
|
group: String(data.group ?? 'misc'),
|
||||||
|
category: String(data.category ?? 'misc'),
|
||||||
|
description: String(data.description ?? ''),
|
||||||
|
updateTime: data['update-time'] == null ? '' : String(data['update-time']),
|
||||||
|
keywords,
|
||||||
|
link: `/api/${slug}`,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.sort((left, right) => left.title.localeCompare(right.title))
|
||||||
|
|
||||||
|
const grouped = [...new Set(entries.map(entry => entry.group))]
|
||||||
|
.sort((left, right) => humanize(left).localeCompare(humanize(right)))
|
||||||
|
.map(group => {
|
||||||
|
const items = entries.filter(entry => entry.group === group)
|
||||||
|
const categories = [...new Set(items.map(entry => entry.category))]
|
||||||
|
.sort((left, right) => humanize(left).localeCompare(humanize(right)))
|
||||||
|
.map(category => ({
|
||||||
|
id: category,
|
||||||
|
label: humanize(category),
|
||||||
|
entries: items.filter(entry => entry.category === category),
|
||||||
|
}))
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: group,
|
||||||
|
label: humanize(group),
|
||||||
|
entryCount: items.length,
|
||||||
|
categories,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const categorySummaries = [...new Set(entries.map(entry => entry.category))]
|
||||||
|
.sort((left, right) => humanize(left).localeCompare(humanize(right)))
|
||||||
|
.map(category => ({
|
||||||
|
id: category,
|
||||||
|
label: humanize(category),
|
||||||
|
entryCount: entries.filter(entry => entry.category === category).length,
|
||||||
|
}))
|
||||||
|
|
||||||
|
return {
|
||||||
|
entries,
|
||||||
|
groups: grouped,
|
||||||
|
categories: categorySummaries,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function readChanges() {
|
||||||
|
return readdirSync(changesDir, { withFileTypes: true })
|
||||||
|
.filter(entry => entry.isFile())
|
||||||
|
.filter(entry => extname(entry.name) === '.md')
|
||||||
|
.filter(entry => entry.name !== 'index.md')
|
||||||
|
.map(entry => basename(entry.name, '.md'))
|
||||||
|
.sort((left, right) => right.localeCompare(left, undefined, { numeric: true }))
|
||||||
|
.map(version => ({
|
||||||
|
version,
|
||||||
|
link: `/changes/${version}`,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
const api = readApiDocs()
|
||||||
|
const changes = readChanges()
|
||||||
|
|
||||||
|
const output = `export const docsData = ${JSON.stringify({ api, changes }, null, 2)}\n`
|
||||||
|
mkdirSync(dirname(outFile), { recursive: true })
|
||||||
|
writeFileSync(outFile, output)
|
||||||
Reference in New Issue
Block a user