Files
Nexus/docs/template.html
T

192 lines
15 KiB
HTML
Raw Normal View History

<!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>
<!-- 文档示例:生产页请使用 /app/vendor/*,勿引外网 CDN -->
<script src="/app/vendor/tailwindcss-browser.js"></script>
<script src="/app/vendor/alpinejs.min.js" defer></script>
<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>