Files
Nexus/docs/template.html
T
Your Name 8c8d2a74d5 docs: add Vercel-style documentation browser
Tailwind CSS + Alpine.js single-page HTML doc viewer with:
- 274 documents embedded (270 md + 4 other file types)
- Dark/light mode with localStorage persistence
- Sidebar navigation with grouped collapsible sections
- Full-text search (filename + content)
- Auto-generated TOC for each document
- Terminal-style code blocks with language labels
- Mobile responsive with slide-out sidebar
- rebuild: python docs/build_html.py

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 07:26:31 +08:00

194 lines
15 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN" class="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Nexus 6.0 Docs</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/12.0.1/marked.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
<script>
tailwind.config={darkMode:'class',theme:{extend:{fontFamily:{sans:['Inter','system-ui','-apple-system','sans-serif'],mono:['JetBrains Mono','Fira Code','Consolas','monospace']},colors:{v:{border:'rgba(255,255,255,0.08)',hover:'rgba(255,255,255,0.06)',active:'rgba(255,255,255,0.10)',dim:'#666',muted:'#888',text:'#ccc',heading:'#ededed'}}}}}
</script>
<style>
[x-cloak]{display:none!important}
.scroll-area{scrollbar-width:thin;scrollbar-color:transparent transparent}
.scroll-area:hover{scrollbar-color:#333 transparent}
.scroll-area::-webkit-scrollbar{width:5px}
.scroll-area::-webkit-scrollbar-thumb{background:transparent;border-radius:9px}
.scroll-area:hover::-webkit-scrollbar-thumb{background:#333}
.scroll-area::-webkit-scrollbar-track{background:transparent}
.prose-v h1{@apply text-[28px] font-bold tracking-[-0.02em] text-v-heading mb-8}
.prose-v h1::after{content:'';display:block;margin-top:20px;border-bottom:1px solid rgba(255,255,255,0.08)}
.prose-v h2{@apply text-[20px] font-semibold tracking-[-0.01em] text-v-heading mt-12 mb-4}
.prose-v h2::after{content:'';display:block;margin-top:12px;border-bottom:1px solid rgba(255,255,255,0.06)}
.prose-v h3{@apply text-[16px] font-semibold text-v-heading mt-8 mb-3}
.prose-v h4{@apply text-[14px] font-semibold text-v-text mt-6 mb-2}
.prose-v p{@apply mb-5 leading-[1.8] text-v-muted text-[14px]}
.prose-v a{@apply text-blue-400 hover:text-blue-300 underline underline-offset-2 decoration-blue-400/30 hover:decoration-blue-300/50 transition-colors}
.prose-v strong{@apply text-v-heading font-semibold}
.prose-v code{@apply bg-white/[0.06] px-[5px] py-[2px] rounded-[4px] text-[13px] font-mono text-[#c9a0ff]}
.prose-v pre{@apply relative bg-[#080808] border border-v-border rounded-[8px] p-0 overflow-hidden mb-6}
.prose-v pre::before{content:attr(data-lang);@apply absolute top-0 right-0 text-[10px] font-mono text-white/20 px-3 py-2 uppercase tracking-wider select-none}
.prose-v pre code{@apply bg-transparent p-5 block text-[13px] leading-[1.7] text-[#aaa] overflow-x-auto}
.prose-v blockquote{@apply border-l-2 border-blue-500/40 pl-5 py-2 mb-5 bg-blue-500/[0.03] text-v-muted text-[14px]}
.prose-v table{@apply w-full text-[13px] mb-6 border border-v-border rounded-lg overflow-hidden}
.prose-v thead{@apply bg-white/[0.03]}
.prose-v th{@apply px-4 py-3 text-left text-[11px] font-medium uppercase tracking-wider text-v-dim border-b border-v-border}
.prose-v td{@apply px-4 py-2.5 border-b border-white/[0.04] text-v-muted}
.prose-v tr:last-child td{@apply border-b-0}
.prose-v ul{@apply list-none pl-0 mb-5 space-y-2}
.prose-v ol{@apply list-none pl-0 mb-5 space-y-2}
.prose-v li{@apply text-v-muted text-[14px] pl-5 relative}
.prose-v li::before{@apply absolute left-0 text-v-dim;content:''}
.prose-v ol>li{counter-increment:item}
.prose-v ol>li::before{content:counter(item) '.'}
.prose-v hr{@apply border-v-border my-10}
.prose-v img{@apply max-w-full rounded-lg border border-v-border}
.nav-item{@apply block px-3 py-[6px] text-[13px] text-v-dim cursor-pointer truncate transition-all duration-100 rounded-md}
.nav-item:hover{@apply text-v-text bg-v-hover}
.nav-item.active{@apply text-white bg-v-active font-medium}
.toc-link{@apply block text-[12px] py-[5px] text-v-dim hover:text-v-text truncate transition-colors duration-100}
.toc-link.active{@apply text-white font-medium}
html:not(.dark) .prose-v h1,html:not(.dark) .prose-v h2,html:not(.dark) .prose-v h3{@apply text-gray-900}
html:not(.dark) .prose-v h4,html:not(.dark) .prose-v strong{@apply text-gray-800}
html:not(.dark) .prose-v p,html:not(.dark) .prose-v li,html:not(.dark) .prose-v td{@apply text-gray-500}
html:not(.dark) .prose-v h1::after,html:not(.dark) .prose-v h2::after{@apply border-gray-200}
html:not(.dark) .prose-v pre{@apply bg-gray-50 border-gray-200}
html:not(.dark) .prose-v pre code{@apply text-gray-700}
html:not(.dark) .prose-v code{@apply bg-gray-100 text-purple-700}
html:not(.dark) .prose-v th{@apply text-gray-400 border-gray-200}
html:not(.dark) .prose-v thead{@apply bg-gray-50}
html:not(.dark) .prose-v td{@apply border-gray-100}
html:not(.dark) .nav-item:hover{@apply bg-gray-100}
html:not(.dark) .nav-item.active{@apply bg-gray-200 text-gray-900}
</style>
</head>
<body class="h-screen overflow-hidden bg-black text-gray-300 font-sans antialiased" x-data="docApp()" x-init="init()" x-cloak>
<div x-show="sidebarOpen" x-transition.opacity.duration.150ms class="fixed inset-0 bg-black/70 backdrop-blur-sm z-30 lg:hidden" @click="sidebarOpen=false"></div>
<div class="flex h-full">
<!-- Sidebar -->
<aside :class="sidebarOpen?'translate-x-0':'-translate-x-full lg:translate-x-0'" class="fixed lg:relative z-40 w-[280px] h-full flex flex-col border-r border-v-border bg-black transition-transform duration-200 ease-out">
<div class="px-5 pt-6 pb-5 shrink-0">
<div class="flex items-center gap-3">
<div class="w-7 h-7 rounded-md bg-gradient-to-tr from-blue-500 via-violet-500 to-fuchsia-500 flex items-center justify-center text-white text-xs font-bold shadow-lg shadow-violet-500/20">N</div>
<div><h1 class="text-[13px] font-semibold text-white tracking-tight">Nexus 6.0</h1><p class="text-[11px] text-v-dim">Documentation</p></div>
</div>
</div>
<div class="px-4 pb-4 shrink-0">
<div class="relative group">
<svg class="absolute left-2.5 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-v-dim group-focus-within:text-v-muted transition-colors" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg>
<input x-model="searchQuery" @keydown.escape="searchQuery=''" type="text" placeholder="Quick search..." class="w-full pl-8 pr-8 py-[7px] text-[13px] rounded-md border border-v-border bg-transparent text-gray-300 placeholder-v-dim focus:outline-none focus:border-white/20 transition-all">
<kbd class="absolute right-2 top-1/2 -translate-y-1/2 text-[10px] text-v-dim border border-v-border rounded px-1.5 py-[1px] font-mono hidden sm:block">⌘K</kbd>
</div>
</div>
<div class="mx-4 h-px bg-v-border"></div>
<nav class="flex-1 overflow-y-auto scroll-area py-3 px-3">
<template x-for="group in filteredGroups" :key="group.name">
<div class="mb-0.5">
<button @click="group.collapsed=!group.collapsed" class="w-full flex items-center gap-2 px-2 py-[7px] text-[11px] font-medium uppercase tracking-[0.08em] text-v-dim hover:text-v-muted transition-colors rounded-md">
<svg :class="group.collapsed?'-rotate-90':''" class="w-[10px] h-[10px] transition-transform duration-150" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"/></svg>
<span x-text="group.icon+' '+group.name"></span>
<span class="ml-auto text-[10px] text-v-dim/60 font-mono" x-text="group.files.length"></span>
</button>
<div x-show="!group.collapsed" x-collapse.duration.150ms>
<div class="ml-1 space-y-px">
<template x-for="f in group.files" :key="f.path">
<div @click="openDoc(f.path)" :title="f.path" :class="{'active':activePath===f.path}" class="nav-item" x-text="f.label"></div>
</template>
</div>
</div>
</div>
</template>
</nav>
<div class="px-4 py-3 border-t border-v-border shrink-0 flex items-center justify-between">
<span class="text-[11px] text-v-dim/50 font-mono" x-text="totalDocs+' docs'"></span>
<button @click="toggleTheme()" class="p-1 rounded-md text-v-dim hover:text-v-muted hover:bg-v-hover transition-all" title="Toggle theme">
<svg x-show="darkMode" class="w-[14px] h-[14px]" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M12 3v2.25m6.364.386l-1.591 1.591M21 12h-2.25m-.386 6.364l-1.591-1.591M12 18.75V21m-4.773-4.227l-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z"/></svg>
<svg x-show="!darkMode" class="w-[14px] h-[14px]" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M21.752 15.002A9.718 9.718 0 0118 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 003 7.5c0 5.385 4.365 9.75 9.75 9.75a9.726 9.726 0 006.002-2.248z"/></svg>
</button>
</div>
</aside>
<!-- Main -->
<main class="flex-1 flex flex-col min-w-0">
<header class="shrink-0 h-11 flex items-center gap-3 px-5 border-b border-v-border bg-black/90 backdrop-blur-md z-10">
<button @click="sidebarOpen=!sidebarOpen" class="lg:hidden p-1 rounded-md text-v-dim hover:text-v-muted hover:bg-v-hover transition-all">
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"/></svg>
</button>
<nav class="flex items-center gap-1 text-[12px] overflow-hidden font-mono">
<a href="#" @click.prevent="goHome()" class="text-v-dim hover:text-v-muted transition-colors">~</a>
<template x-for="(part,i) in breadcrumb" :key="i">
<span class="text-v-dim/40 mx-0.5">/</span>
<span :class="i===breadcrumb.length-1?'text-v-text':'text-v-dim'" x-text="part"></span>
</template>
</nav>
<div class="ml-auto text-[11px] text-v-dim/40 font-mono hidden sm:block" x-text="fileStats"></div>
</header>
<div class="flex-1 flex overflow-hidden">
<div class="flex-1 overflow-y-auto scroll-area" id="main-scroll">
<div class="max-w-[680px] mx-auto px-6 py-14 lg:px-0">
<div x-show="!activePath" class="flex flex-col items-center justify-center py-44 text-center select-none">
<div class="w-14 h-14 rounded-xl bg-gradient-to-tr from-blue-500/10 via-violet-500/10 to-fuchsia-500/10 border border-v-border flex items-center justify-center mb-8">
<svg class="w-6 h-6 text-v-dim" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z"/></svg>
</div>
<h2 class="text-xl font-semibold text-white tracking-tight">Nexus Documentation</h2>
<p class="text-[13px] text-v-dim mt-2">Select a document or press <kbd class="text-[11px] border border-v-border rounded px-1.5 py-[1px] font-mono ml-1">⌘K</kbd></p>
</div>
<div x-show="activePath" class="prose-v" x-html="renderedContent"></div>
</div>
</div>
<aside x-show="tocItems.length>1" x-transition.duration.150ms class="hidden xl:block w-[180px] shrink-0 border-l border-v-border overflow-y-auto scroll-area py-14 pl-5 pr-3">
<div class="text-[10px] font-medium uppercase tracking-[0.1em] text-v-dim mb-4">On this page</div>
<nav class="space-y-0.5 border-l border-v-border/50 ml-0.5">
<template x-for="item in tocItems" :key="item.id">
<a :href="'#'+item.id" @click.prevent="scrollToHeading(item.id)" :class="['toc-link pl-3',{'active border-l-2 -ml-px border-white/30':activeToc===item.id}]" :style="activeToc!==item.id?'padding-left:13px':''" x-text="item.text"></a>
</template>
</nav>
</aside>
</div>
</main>
</div>
<script>
// __DATA_PLACEHOLDER__
function docApp(){
return {
navGroups:NAV_GROUPS,contentMap:CONTENT_MAP,
searchQuery:'',activePath:null,renderedContent:'',
breadcrumb:['docs'],tocItems:[],activeToc:null,darkMode:true,sidebarOpen:false,
get totalDocs(){return Object.keys(this.contentMap).length},
get fileStats(){if(!this.activePath)return'';const c=this.contentMap[this.activePath]||'';return c.split('\n').length+'L · '+(c.length/1024).toFixed(1)+'K'},
get filteredGroups(){
const q=this.searchQuery.toLowerCase();
return this.navGroups.map(g=>{const files=q?g.files.filter(f=>f.path.toLowerCase().includes(q)||(this.contentMap[f.path]||'').toLowerCase().includes(q)):g.files;return{...g,files,collapsed:files.length===0?true:g.collapsed}}).filter(g=>g.files.length>0);
},
init(){
const s=localStorage.getItem('nexus-docs-theme');this.darkMode=s?s==='dark':true;this.applyTheme();
document.addEventListener('keydown',e=>{if((e.ctrlKey||e.metaKey)&&e.key==='k'){e.preventDefault();document.querySelector('input[type=text]')?.focus()}});
this.$nextTick(()=>{const el=document.getElementById('main-scroll');if(el)el.addEventListener('scroll',()=>this.updateActiveToc())});
},
toggleTheme(){this.darkMode=!this.darkMode;this.applyTheme();localStorage.setItem('nexus-docs-theme',this.darkMode?'dark':'light')},
applyTheme(){document.documentElement.classList.toggle('dark',this.darkMode);document.body.className=this.darkMode?'h-screen overflow-hidden bg-black text-gray-300 font-sans antialiased':'h-screen overflow-hidden bg-white text-gray-700 font-sans antialiased'},
openDoc(path){this.activePath=path;this.sidebarOpen=false;this.renderedContent=marked.parse(this.contentMap[path]||'*Not found*');this.breadcrumb=path.replace('.md','').split('/');this.$nextTick(()=>{this.buildToc();document.getElementById('main-scroll')?.scrollTo(0,0)})},
goHome(){this.activePath=null;this.renderedContent='';this.breadcrumb=['docs'];this.tocItems=[]},
buildToc(){const c=document.querySelector('.prose-v');if(!c){this.tocItems=[];return}this.tocItems=Array.from(c.querySelectorAll('h1,h2,h3,h4')).map((h,i)=>{h.id='toc-'+i;return{id:'toc-'+i,text:h.textContent,level:parseInt(h.tagName[1])}});this.activeToc=this.tocItems[0]?.id||null},
scrollToHeading(id){document.getElementById(id)?.scrollIntoView({behavior:'smooth',block:'start'});this.activeToc=id},
updateActiveToc(){for(let i=this.tocItems.length-1;i>=0;i--){const el=document.getElementById(this.tocItems[i].id);if(el&&el.getBoundingClientRect().top<100){this.activeToc=this.tocItems[i].id;return}}this.activeToc=this.tocItems[0]?.id||null}
};
}
const renderer=new marked.Renderer();
renderer.code=function(code,lang){const a=lang?` data-lang="${lang}"`:'';const h=lang&&hljs.getLanguage(lang)?hljs.highlight(code,{language:lang}).value:hljs.highlightAuto(code).value;return`<pre${a}><code>${h}</code></pre>`};
marked.setOptions({renderer,breaks:true,gfm:true});
</script>
</body>
</html>