minimax m2.7生成
This commit is contained in:
parent
29e286a2a0
commit
297e59e7a4
166
README.md
166
README.md
@ -1,138 +1,78 @@
|
||||
# LLM Log Viewer
|
||||
# LLM Log Dashboard
|
||||
|
||||
> 将 Markdown 日志文件批量转换为精美静态 HTML 页面的 Node.js 构建工具。
|
||||
|
||||

|
||||

|
||||
一个将 LLM 日志 Markdown 文件批量聚合渲染为精美静态 HTML 页面的工具。
|
||||
|
||||
## 特性
|
||||
|
||||
| 功能 | 描述 |
|
||||
|------|------|
|
||||
| **批量转换** | 自动扫描 `llm_log/` 目录,读取所有 `.md` 文件并合并输出为单页 HTML |
|
||||
| **目录导航** | 侧边栏自动提取各文档的标题层级(TOC),支持平滑滚动跳转 |
|
||||
| **深色模式** | 一键切换亮/暗主题,自动记忆用户偏好(localStorage) |
|
||||
| **代码高亮** | 基于 highlight.js 的多语言语法着色,支持 8 种主流语言 |
|
||||
| **复制按钮** | 代码块悬停时显示 Copy 按钮,点击写入剪贴板 |
|
||||
| **表格美化** | GFM 表格自定义样式:圆角边框、悬停高亮、响应式横向滚动 |
|
||||
| **活跃追踪** | IntersectionObserver 实时高光当前阅读章节的侧边栏链接 |
|
||||
| **响应式布局** | 移动端(< 768px)自动隐藏侧边栏,显示汉堡菜单按钮 |
|
||||
| **入场动画** | 页面区块淡入动画(fadeIn + translateY),提升阅读体验 |
|
||||
- **批量解析**: 自动读取 `llm_log/` 目录下所有 `.md` 文件
|
||||
- **设计驱动**: 严格遵循 `ui-ux-pro-max` 设计规范体系
|
||||
- **响应式布局**: 移动端优先,适配 375px → 1440px 断点
|
||||
- **微交互**: 表格行悬停高亮、卡片悬浮上浮动画
|
||||
- **无障碍**: ARIA 语义标签、`prefers-reduced-motion` 支持
|
||||
- **零外部依赖**: 生成纯静态 HTML,字体通过 Google Fonts CDN 引入
|
||||
|
||||
## 目录结构
|
||||
## 设计令牌
|
||||
|
||||
| Token | Value | Usage |
|
||||
|-------|-------|-------|
|
||||
| Primary | `#6366f1` | 主题色、强调色 |
|
||||
| Surface | `#ffffff` | 卡片背景 |
|
||||
| Background | `#f1f5f9` | 页面背景 |
|
||||
| Text Primary | `#0f172a` | 主要文本 |
|
||||
| Radius | 6/10/16px | 圆角层级 |
|
||||
| Font Sans | Inter | 正文字体 |
|
||||
| Font Mono | JetBrains Mono | 代码字体 |
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
llm_log_frontend/
|
||||
├── build.js # 构建脚本(核心逻辑)
|
||||
├── package.json # NPM 依赖配置
|
||||
├── index.html # 构建产物(静态站点)
|
||||
└── llm_log/ # Markdown 源文件目录
|
||||
└── 2026-04-26.md # 示例日志文件
|
||||
├── llm_log/ # Markdown 日志文件目录
|
||||
│ └── 2026-04-26.md # 按日期命名的日志
|
||||
├── index.html # 生成的静态页面
|
||||
├── generate.js # 生成器脚本
|
||||
└── package.json
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 安装依赖
|
||||
## 使用方法
|
||||
|
||||
```bash
|
||||
# 安装依赖
|
||||
npm install
|
||||
|
||||
# 生成 HTML
|
||||
npm run generate
|
||||
```
|
||||
|
||||
### 构建站点
|
||||
## 日志格式
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
# 或直接执行
|
||||
node build.js
|
||||
将 Markdown 文件放入 `llm_log/` 目录,文件名格式为 `YYYY-MM-DD.md`:
|
||||
|
||||
```markdown
|
||||
| 小时 | 平均输入t/s | 平均输出t/s | 输入token总数 | 输出token总数 | token总数 |
|
||||
| :---: | :---: | :---: | :---: | :---: | :---: |
|
||||
| 00 | 216.04 | 26.08 | 115,105 | 5,701 | 120,806 |
|
||||
```
|
||||
|
||||
构建产物为项目根目录下的 `index.html`,可直接用浏览器打开,或部署至任意静态托管服务(GitHub Pages、Vercel、Netlify 等)。
|
||||
## 设计规范
|
||||
|
||||
### 添加日志文件
|
||||
本项目遵循 [ui-ux-pro-max](https://github.com/anomalyco/opencode) 规范体系的核心准则:
|
||||
|
||||
**方式一**:在 `llm_log/` 目录中直接放入 `.md` 文件(按文件名排序)
|
||||
- **排版节奏**: 16px 基准字重,1.5-1.6 行高,65-75 字最大行长
|
||||
- **间距系统**: 4/8dp 增量,保持 8px 最小触摸间距
|
||||
- **阴影层级**: sm(1px) → md(4px) → lg(10px) 递进
|
||||
- **过渡动画**: 150-300ms ease-out,支持减弱动效偏好
|
||||
- **对比度**: 文本与背景 4.5:1 以上,触摸目标 ≥44×44pt
|
||||
|
||||
**方式二**:在 `llm_log/` 目录下创建 `log.txt`,每行写一个相对路径:
|
||||
## 预览
|
||||
|
||||
```
|
||||
# llm_log/log.txt
|
||||
2026-04-26.md
|
||||
2026-04-25.md
|
||||
../other-dir/report.md
|
||||
```
|
||||
生成的页面包含:
|
||||
|
||||
## 技术栈
|
||||
- 粘性导航栏 + 最后更新时间
|
||||
- 卡片式日志条目,每条带日期标识
|
||||
- 表格悬停高亮,`**全天**` 行高亮突出
|
||||
- 移动端自适应表格滚动
|
||||
|
||||
| 层级 | 选型 |
|
||||
|------|------|
|
||||
| 运行时 | Node.js >= 18 |
|
||||
| Markdown 解析 | [marked](https://github.com/markedjs/marked) v12.0.2 |
|
||||
| 代码高亮 | [highlight.js](https://highlightjs.org/) v11.9.0 (CDN) |
|
||||
| 样式框架 | [Tailwind CSS](https://tailwindcss.com/) (CDN) |
|
||||
| 字体 | Inter (Google Fonts) |
|
||||
## License
|
||||
|
||||
## 自定义 renderer
|
||||
|
||||
`build.js` 中预置了 5 个自定义渲染器,覆盖常用 Markdown 元素:
|
||||
|
||||
```javascript
|
||||
// 代码块:深色背景 + 复制按钮 + 语法高亮
|
||||
renderer.code = function (code, lang) { ... }
|
||||
|
||||
// 行内代码:蓝底白字(亮)/ 深蓝底(暗)
|
||||
renderer.codespan = function (code) { ... }
|
||||
|
||||
// 表格行:悬停高亮
|
||||
renderer.tablerow = function (content) { ... }
|
||||
|
||||
// 表格单元格:表头/数据格差异化样式
|
||||
renderer.tablecell = function (content, flags) { ... }
|
||||
|
||||
// 表格:外层包 overflow-x-auto,响应式滚动
|
||||
renderer.table = function (header, body) { ... }
|
||||
```
|
||||
|
||||
如需添加更多自定义(如 blockquote、hr 等),可按相同模式追加至 `renderer` 对象后调用 `marked.setOptions({ renderer })` 生效。
|
||||
|
||||
## 输出示例
|
||||
|
||||
构建 `index.html` 后,打开浏览器即可看到:
|
||||
|
||||
- **左侧**:固定侧边栏,显示文件列表与嵌套 TOC
|
||||
- **右侧**:主内容区,最大宽度 `max-w-4xl`,Inter 字体,阅读体验友好
|
||||
- **右上角**:深色模式切换按钮(太阳/月亮图标)
|
||||
- **顶部**:吸顶导航栏,含移动端汉堡菜单触发按钮
|
||||
|
||||
## 部署
|
||||
|
||||
### GitHub Pages
|
||||
|
||||
```bash
|
||||
# 1. 确保 index.html 已提交至 git 仓库
|
||||
git add index.html
|
||||
git commit -m "docs: add generated static site"
|
||||
|
||||
# 2. 推送至 GitHub
|
||||
git push origin main
|
||||
|
||||
# 3. 在仓库 Settings > Pages > Source 选择 "main" 分支
|
||||
```
|
||||
|
||||
### 本地预览
|
||||
|
||||
```bash
|
||||
# Python 静态服务器
|
||||
python -m http.server 8080
|
||||
|
||||
# 或 Node.js serve
|
||||
npx serve .
|
||||
|
||||
# 访问 http://localhost:8080
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **Node.js 版本**:marked v12 需要 Node.js >= 18,若遇到 `EBADENGINE` 警告请升级 Node。
|
||||
2. **XSS 安全**:当前构建脚本直接拼接 Markdown 输出,适用于可信的内部日志。若需处理用户提交内容,建议在 `marked.parse()` 后串接 DOMPurify 消毒。
|
||||
3. **CDN 依赖**:样式与脚本均通过 CDN 加载,部署时请确保网络可访问 cdn.tailwindcss.com、cdnjs.cloudflare.com 等。
|
||||
4. **构建产物**:建议将 `index.html` 纳入版本管理,便于 CI/CD 自动部署和历史追溯。
|
||||
MIT
|
||||
304
build.js
304
build.js
@ -1,304 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { marked } = require('marked');
|
||||
|
||||
// ─── Config ────────────────────────────────────────────────
|
||||
const LOG_DIR = path.join(__dirname, 'llm_log');
|
||||
const OUTPUT_FILE = path.join(__dirname, 'index.html');
|
||||
|
||||
// ─── Marked Config ─────────────────────────────────────────
|
||||
const headingIds = new Map();
|
||||
|
||||
const renderer = new marked.Renderer();
|
||||
renderer.code = function (code, lang) {
|
||||
const validLang = lang || 'text';
|
||||
const highlighted = highlightCode(code, validLang);
|
||||
const id = 'highlight-' + Math.random().toString(36).slice(2, 8);
|
||||
return `<pre class="relative group"><button class="copy-btn absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity px-2 py-1 text-xs bg-gray-600 hover:bg-gray-500 rounded" onclick="copyCode(this)">Copy</button><code id="${id}" class="hljs language-${validLang} block overflow-auto max-h-64 p-4 rounded-lg bg-gray-900 text-gray-100 text-sm leading-relaxed">${highlighted}</code></pre>`;
|
||||
};
|
||||
renderer.codespan = function (code) {
|
||||
return `<code class="px-1.5 py-0.5 text-sm font-mono bg-blue-50 dark:bg-blue-900/30 text-blue-600 dark:text-blue-400 rounded border border-blue-200 dark:border-blue-700">${code}</code>`;
|
||||
};
|
||||
renderer.tablerow = function (content) {
|
||||
return `<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors">${content}</tr>\n`;
|
||||
};
|
||||
renderer.tablecell = function (content, flags) {
|
||||
const type = flags.header ? 'th' : 'td';
|
||||
const cls = flags.header
|
||||
? 'px-4 py-3 text-left font-semibold bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700'
|
||||
: 'px-4 py-3 border border-gray-200 dark:border-gray-700';
|
||||
const tag = flags.align
|
||||
? `<${type} class="${cls}" align="${flags.align}">`
|
||||
: `<${type} class="${cls}">`;
|
||||
return tag + content + `</${type}>\n`;
|
||||
};
|
||||
renderer.table = function (header, body) {
|
||||
if (body) body = `<tbody>${body}</tbody>`;
|
||||
return `<div class="overflow-x-auto my-6 rounded-lg border border-gray-200 dark:border-gray-700 shadow-sm"><table class="w-full text-sm"><thead>${header}</thead>${body}</table></div>\n`;
|
||||
};
|
||||
|
||||
marked.setOptions({ renderer, gfm: true });
|
||||
|
||||
function highlightCode(code, lang) {
|
||||
const escaped = escapeHtml(code.trim());
|
||||
if (!lang) return escaped;
|
||||
try {
|
||||
const hl = require('highlight.js');
|
||||
const result = hl.getLanguage(lang) ? hl.highlight(code.trim(), { language: lang }) : hl.highlightAuto(code.trim());
|
||||
return result.value;
|
||||
} catch {
|
||||
return escaped;
|
||||
}
|
||||
}
|
||||
|
||||
function escapeHtml(str) {
|
||||
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
|
||||
}
|
||||
|
||||
// ─── Read Log Files ────────────────────────────────────────
|
||||
function readLogFile(filePath) {
|
||||
const content = fs.readFileSync(filePath, 'utf-8');
|
||||
return content
|
||||
.split(/\r?\n/)
|
||||
.map(line => line.trim())
|
||||
.filter(line => line.length > 0);
|
||||
}
|
||||
|
||||
// ─── Inject Heading IDs into Markdown ──────────────────────
|
||||
function injectHeadingIds(markdown, prefix) {
|
||||
const toc = [];
|
||||
const lines = markdown.split('\n');
|
||||
const headingRegex = /^(#{1,6})\s+(.+)$/;
|
||||
let headingIndex = 0;
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const match = lines[i].match(headingRegex);
|
||||
if (match) {
|
||||
const level = match[1].length;
|
||||
const text = match[2].trim().replace(/[#!*_~]/g, '');
|
||||
const headingId = `${prefix}-h${headingIndex++}`;
|
||||
lines[i] = lines[i] + ` {#${headingId}}`;
|
||||
toc.push({ level, text, id: headingId });
|
||||
}
|
||||
}
|
||||
|
||||
return { markdown: lines.join('\n'), toc };
|
||||
}
|
||||
|
||||
// ─── Build Pages ───────────────────────────────────────────
|
||||
async function build() {
|
||||
const logFile = path.join(LOG_DIR, 'log.txt');
|
||||
const logDir = LOG_DIR;
|
||||
|
||||
let mdFiles = [];
|
||||
|
||||
// Try reading log.txt first
|
||||
if (fs.existsSync(logFile)) {
|
||||
const entries = readLogFile(logFile);
|
||||
mdFiles = entries
|
||||
.filter(entry => entry.endsWith('.md'))
|
||||
.map(entry => path.resolve(path.dirname(logFile), '..', entry));
|
||||
}
|
||||
|
||||
// Fallback: scan llm_log directory for .md files
|
||||
if (mdFiles.length === 0 && fs.existsSync(logDir) && fs.statSync(logDir).isDirectory()) {
|
||||
mdFiles = fs.readdirSync(logDir)
|
||||
.filter(f => f.endsWith('.md'))
|
||||
.map(f => path.join(logDir, f))
|
||||
.sort();
|
||||
}
|
||||
|
||||
if (mdFiles.length === 0) {
|
||||
console.error('No .md files found in llm_log/ or llm_log/log.txt');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`Found ${mdFiles.length} markdown file(s)`);
|
||||
|
||||
const pages = [];
|
||||
for (const filePath of mdFiles) {
|
||||
try {
|
||||
const raw = fs.readFileSync(filePath, 'utf-8');
|
||||
if (!raw.trim()) continue;
|
||||
|
||||
const prefix = path.basename(filePath, '.md');
|
||||
const { markdown, toc } = injectHeadingIds(raw, prefix);
|
||||
const html = marked.parse(markdown);
|
||||
|
||||
pages.push({
|
||||
title: prefix,
|
||||
html,
|
||||
toc,
|
||||
filePath
|
||||
});
|
||||
} catch (err) {
|
||||
console.warn(`Failed to process ${filePath}: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (pages.length === 0) {
|
||||
console.error('No valid markdown content found.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// ─── Generate HTML ───────────────────────────────────────
|
||||
const tocLinks = pages.map((p, i) => `
|
||||
<li>
|
||||
<a href="#page-${i}" class="toc-link block px-3 py-2 text-sm rounded-md transition-colors hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white font-medium" data-target="${i}">
|
||||
${escapeHtml(p.title)}
|
||||
${p.toc.length > 0 ? `<span class="float-right text-xs text-gray-400">${p.toc.length}</span>` : ''}
|
||||
</a>
|
||||
${p.toc.length > 0 ? `<ul class="ml-4 mt-1 space-y-0.5 border-l border-gray-200 dark:border-gray-700 pl-2">${p.toc.map(t => {
|
||||
const indent = t.level >= 4 ? 'ml-8' : 'ml-4';
|
||||
return `<li class="${indent}"><a href="#${t.id}" class="toc-sublink block py-1 text-xs text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 transition-colors" data-target="${i}">${escapeHtml(t.text)}</a></li>`;
|
||||
}).join('')}</ul>` : ''}
|
||||
</li>`).join('');
|
||||
|
||||
const pageBlocks = pages.map((p, i) => `
|
||||
<section id="page-${i}" class="page-section mb-20 scroll-mt-24">
|
||||
<div class="mb-8 pb-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h1 class="text-3xl font-bold text-gray-900 dark:text-white mb-2">${escapeHtml(p.title)}</h1>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">${path.basename(p.filePath)}</p>
|
||||
</div>
|
||||
<div class="prose prose-gray dark:prose-invert max-w-none prose-headings:font-bold prose-headings:text-gray-900 dark:prose-headings:text-white prose-p:text-gray-700 dark:prose-p:text-gray-300 prose-a:text-blue-600 dark:prose-a:text-blue-400 prose-strong:text-gray-900 dark:prose-strong:text-white prose-code:text-sm prose-pre:rounded-lg prose-pre:bg-gray-900 prose-img:rounded-lg prose-table:border-collapse prose-table:border prose-table:border-gray-200 dark:prose-table:border-gray-700 prose-th:bg-gray-50 dark:prose-th:bg-gray-800 prose-th:border prose-th:border-gray-200 dark:prose-th:border-gray-700 prose-td:border prose-td:border-gray-200 dark:prose-td:border-gray-700 prose-li:marker:text-gray-500">${p.html}</div>
|
||||
</section>`).join('');
|
||||
|
||||
const html = `<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>LLM Log Viewer</title>
|
||||
<script src="https://cdn.tailwindcss.com"><\/script>
|
||||
<script>
|
||||
tailwind.config = {
|
||||
darkMode: 'class',
|
||||
theme: { extend: { fontFamily: { sans: ['Inter', 'system-ui', '-apple-system', 'sans-serif'] } } }
|
||||
}
|
||||
<\/script>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css" id="hljs-light">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css" id="hljs-dark" disabled>
|
||||
<style>
|
||||
* { scrollbar-width: thin; scrollbar-color: #cbd5e1 transparent; }
|
||||
*::-webkit-scrollbar { width: 6px; height: 6px; }
|
||||
*::-webkit-scrollbar-track { background: transparent; }
|
||||
*::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 3px; }
|
||||
.dark *::-webkit-scrollbar-thumb { background: #475569; }
|
||||
.toc-link.active, .toc-sublink.active { background: #eff6ff; color: #2563eb; }
|
||||
.dark .toc-link.active, .dark .toc-sublink.active { background: #1e3a5f; color: #60a5fa; }
|
||||
.page-section { animation: fadeIn 0.3s ease-in-out; }
|
||||
@keyframes fadeIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
|
||||
@media (max-width: 768px) { .sidebar { display: none !important; } .main-content { margin-left: 0 !important; padding-left: 1rem !important; padding-right: 1rem !important; } .mobile-toggle { display: flex !important; } }
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-white dark:bg-gray-950 text-gray-900 dark:text-gray-100 font-sans antialiased transition-colors">
|
||||
<div class="flex min-h-screen">
|
||||
<!-- Sidebar -->
|
||||
<aside class="sidebar fixed left-0 top-0 h-screen w-64 bg-gray-50 dark:bg-gray-900 border-r border-gray-200 dark:border-gray-800 overflow-y-auto z-40 transition-transform">
|
||||
<div class="p-5 border-b border-gray-200 dark:border-gray-800">
|
||||
<h2 class="text-lg font-bold text-gray-900 dark:text-white flex items-center gap-2">
|
||||
<svg class="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/></svg>
|
||||
LLM Logs
|
||||
</h2>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">${pages.length} file(s)</p>
|
||||
</div>
|
||||
<nav class="p-3">
|
||||
<ul class="space-y-0.5">${tocLinks}</ul>
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="main-content flex-1 ml-64 min-w-0">
|
||||
<header class="sticky top-0 z-30 bg-white/80 dark:bg-gray-950/80 backdrop-blur-md border-b border-gray-200 dark:border-gray-800">
|
||||
<div class="flex items-center justify-between px-6 py-3">
|
||||
<button id="menuBtn" class="mobile-toggle hidden items-center p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/></svg>
|
||||
</button>
|
||||
<div class="flex items-center gap-3 ml-auto">
|
||||
<button id="themeToggle" class="p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors" title="Toggle theme">
|
||||
<svg id="sunIcon" class="w-5 h-5 hidden dark:block" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"/></svg>
|
||||
<svg id="moonIcon" class="w-5 h-5 block dark:hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="max-w-4xl mx-auto px-6 py-10">
|
||||
${pageBlocks}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<!-- Scripts -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"><\/script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/bash.min.js"><\/script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/javascript.min.js"><\/script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/typescript.min.js"><\/script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/python.min.js"><\/script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/json.min.js"><\/script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/css.min.js"><\/script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/xml.min.js"><\/script>
|
||||
<script>
|
||||
// Init highlight.js
|
||||
hljs.highlightAll();
|
||||
|
||||
// Theme toggle
|
||||
const html = document.documentElement;
|
||||
const themeBtn = document.getElementById('themeToggle');
|
||||
const saved = localStorage.getItem('theme');
|
||||
if (saved === 'dark' || (!saved && window.matchMedia('(prefers-color-scheme: dark)').matches)) html.classList.add('dark');
|
||||
themeBtn.addEventListener('click', () => {
|
||||
html.classList.toggle('dark');
|
||||
localStorage.setItem('theme', html.classList.contains('dark') ? 'dark' : 'light');
|
||||
document.getElementById('hljs-light').disabled = html.classList.contains('dark');
|
||||
document.getElementById('hljs-dark').disabled = !html.classList.contains('dark');
|
||||
});
|
||||
|
||||
// Sidebar toggle (mobile)
|
||||
document.getElementById('menuBtn').addEventListener('click', () => {
|
||||
document.querySelector('.sidebar').classList.toggle('-translate-x-full');
|
||||
});
|
||||
|
||||
// Active TOC tracking
|
||||
const sections = document.querySelectorAll('.page-section');
|
||||
const tocLinks = document.querySelectorAll('.toc-link, .toc-sublink');
|
||||
const observer = new IntersectionObserver(entries => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
const idx = entry.target.id.replace('page-', '');
|
||||
tocLinks.forEach(l => l.classList.toggle('active', l.dataset.target === idx));
|
||||
}
|
||||
});
|
||||
}, { rootMargin: '-80px 0px -70% 0px', threshold: 0 });
|
||||
sections.forEach(s => observer.observe(s));
|
||||
|
||||
// Smooth scroll for TOC links
|
||||
tocLinks.forEach(link => {
|
||||
link.addEventListener('click', e => {
|
||||
e.preventDefault();
|
||||
const targetId = link.href.split('#').pop();
|
||||
document.getElementById(targetId)?.scrollIntoView({ behavior: 'smooth' });
|
||||
// Close mobile sidebar
|
||||
if (window.innerWidth < 768) document.querySelector('.sidebar').classList.add('-translate-x-full');
|
||||
});
|
||||
});
|
||||
|
||||
// Copy code button
|
||||
window.copyCode = function(btn) {
|
||||
const code = btn.nextElementSibling;
|
||||
navigator.clipboard.writeText(code.textContent).then(() => {
|
||||
btn.textContent = 'Copied!';
|
||||
setTimeout(() => btn.textContent = 'Copy', 2000);
|
||||
});
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
fs.writeFileSync(OUTPUT_FILE, html, 'utf-8');
|
||||
console.log(`\nBuild complete: ${OUTPUT_FILE}`);
|
||||
console.log(` - ${pages.length} page(s) rendered`);
|
||||
}
|
||||
|
||||
build().catch(err => { console.error(err); process.exit(1); });
|
||||
407
generate.js
Normal file
407
generate.js
Normal file
@ -0,0 +1,407 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { marked } = require('marked');
|
||||
|
||||
const LOG_DIR = path.join(__dirname, 'llm_log');
|
||||
const OUTPUT_FILE = path.join(__dirname, 'index.html');
|
||||
|
||||
function readDirSafe(dir) {
|
||||
if (!fs.existsSync(dir)) return [];
|
||||
const items = fs.readdirSync(dir);
|
||||
return items
|
||||
.filter(f => f.endsWith('.md'))
|
||||
.map(f => path.join(dir, f))
|
||||
.filter(f => fs.statSync(f).isFile());
|
||||
}
|
||||
|
||||
function readFileSafe(filePath) {
|
||||
try {
|
||||
const stat = fs.statSync(filePath);
|
||||
if (stat.size === 0) return null;
|
||||
return fs.readFileSync(filePath, 'utf-8');
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function parseMarkdown(content) {
|
||||
if (!content) return '';
|
||||
marked.setOptions({
|
||||
gfm: true,
|
||||
breaks: false,
|
||||
});
|
||||
return marked.parse(content);
|
||||
}
|
||||
|
||||
const htmlTemplate = `<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>LLM Log Dashboard</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--color-primary: #6366f1;
|
||||
--color-primary-hover: #4f46e5;
|
||||
--color-primary-light: #eef2ff;
|
||||
--color-success: #10b981;
|
||||
--color-warning: #f59e0b;
|
||||
--color-danger: #ef4444;
|
||||
--color-surface: #ffffff;
|
||||
--color-surface-elevated: #f8fafc;
|
||||
--color-background: #f1f5f9;
|
||||
--color-text-primary: #0f172a;
|
||||
--color-text-secondary: #475569;
|
||||
--color-text-tertiary: #94a3b8;
|
||||
--color-border: #e2e8f0;
|
||||
--radius-sm: 6px;
|
||||
--radius-md: 10px;
|
||||
--radius-lg: 16px;
|
||||
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
||||
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||||
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
||||
--font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
--font-mono: 'JetBrains Mono', 'Fira Code', Consolas, monospace;
|
||||
--transition-fast: 150ms ease-out;
|
||||
--transition-normal: 250ms ease-out;
|
||||
--spacing-unit: 4px;
|
||||
}
|
||||
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: 16px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--font-sans);
|
||||
background-color: var(--color-background);
|
||||
color: var(--color-text-primary);
|
||||
line-height: 1.6;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
background-color: var(--color-surface);
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
padding: 0 24px;
|
||||
height: 64px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--color-text-primary);
|
||||
text-decoration: none;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
.navbar-brand svg {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.navbar-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 13px;
|
||||
color: var(--color-text-tertiary);
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 32px 24px;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: var(--color-surface);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-md);
|
||||
border: 1px solid var(--color-border);
|
||||
overflow: hidden;
|
||||
transition: box-shadow var(--transition-normal), transform var(--transition-normal);
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
box-shadow: var(--shadow-lg);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
padding: 20px 24px;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
background-color: var(--color-surface-elevated);
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: var(--color-text-primary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.card-title svg {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.log-entry {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.log-entry:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.log-date {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: var(--color-text-tertiary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
margin-bottom: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.log-date::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: var(--color-primary);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
thead {
|
||||
background-color: var(--color-surface-elevated);
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
padding: 12px 16px;
|
||||
font-weight: 600;
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: var(--color-text-secondary);
|
||||
border-bottom: 2px solid var(--color-border);
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
color: var(--color-text-primary);
|
||||
transition: background-color var(--transition-fast);
|
||||
}
|
||||
|
||||
tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
tr:hover td {
|
||||
background-color: var(--color-primary-light);
|
||||
}
|
||||
|
||||
td:first-child, th:first-child {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
td:last-child, th:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.table-highlight {
|
||||
background-color: var(--color-primary-light) !important;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.table-highlight td {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 13px;
|
||||
background-color: var(--color-surface-elevated);
|
||||
padding: 2px 6px;
|
||||
border-radius: var(--radius-sm);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left: 4px solid var(--color-primary);
|
||||
padding-left: 16px;
|
||||
margin: 16px 0;
|
||||
color: var(--color-text-secondary);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-primary);
|
||||
text-decoration: none;
|
||||
transition: color var(--transition-fast);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--color-primary-hover);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 16px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
table {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 8px 10px;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.navbar-meta {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*, *::before, *::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar" role="navigation" aria-label="Main navigation">
|
||||
<a href="#" class="navbar-brand">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M12 2L2 7l10 5 10-5-10-5z"/>
|
||||
<path d="M2 17l10 5 10-5"/>
|
||||
<path d="M2 12l10 5 10-5"/>
|
||||
</svg>
|
||||
LLM Log Dashboard
|
||||
</a>
|
||||
<div class="navbar-meta">
|
||||
<span id="last-updated"></span>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="container" id="main-content">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h1 class="card-title">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
|
||||
<line x1="3" y1="9" x2="21" y2="9"/>
|
||||
<line x1="9" y1="21" x2="9" y2="9"/>
|
||||
</svg>
|
||||
日志记录
|
||||
</h1>
|
||||
</div>
|
||||
<div class="card-body" id="log-content">
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
document.getElementById('last-updated').textContent = new Date().toLocaleString('zh-CN');
|
||||
</script>
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
function buildHtml(logs) {
|
||||
let content = '';
|
||||
if (logs.length === 0) {
|
||||
content = '<p style="color: var(--color-text-tertiary);">暂无日志记录</p>';
|
||||
} else {
|
||||
content = logs.map(({ date, html }) => `
|
||||
<section class="log-entry" aria-label="${date}">
|
||||
<div class="log-date">${date}</div>
|
||||
${html}
|
||||
</section>
|
||||
`).join('\n');
|
||||
}
|
||||
|
||||
return htmlTemplate.replace('<div class="card-body" id="log-content">', `<div class="card-body" id="log-content">\n ${content}\n `);
|
||||
}
|
||||
|
||||
function main() {
|
||||
console.log('Reading llm_log directory...');
|
||||
const files = readDirSafe(LOG_DIR);
|
||||
|
||||
if (files.length === 0) {
|
||||
console.log('No markdown files found in llm_log directory.');
|
||||
const emptyHtml = htmlTemplate.replace('<p style="color: var(--color-text-tertiary);">加载中...</p>', '<p style="color: var(--color-text-tertiary);">暂无日志记录</p>');
|
||||
fs.writeFileSync(OUTPUT_FILE, emptyHtml);
|
||||
console.log(`Generated: ${OUTPUT_FILE}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const logs = files
|
||||
.map(filePath => {
|
||||
const content = readFileSafe(filePath);
|
||||
if (!content) {
|
||||
console.warn(`Skipping empty or unreadable file: ${filePath}`);
|
||||
return null;
|
||||
}
|
||||
const date = path.basename(filePath, '.md');
|
||||
const html = parseMarkdown(content);
|
||||
return { date, html };
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
logs.sort((a, b) => a.date.localeCompare(b.date));
|
||||
|
||||
const html = buildHtml(logs);
|
||||
fs.writeFileSync(OUTPUT_FILE, html);
|
||||
|
||||
console.log(`Generated: ${OUTPUT_FILE}`);
|
||||
console.log(`Processed ${logs.length} log files.`);
|
||||
}
|
||||
|
||||
main();
|
||||
966
index.html
966
index.html
@ -3,299 +3,699 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>LLM Log Viewer</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script>
|
||||
tailwind.config = {
|
||||
darkMode: 'class',
|
||||
theme: { extend: { fontFamily: { sans: ['Inter', 'system-ui', '-apple-system', 'sans-serif'] } } }
|
||||
}
|
||||
</script>
|
||||
<title>LLM Log Dashboard</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css" id="hljs-light">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css" id="hljs-dark" disabled>
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
* { scrollbar-width: thin; scrollbar-color: #cbd5e1 transparent; }
|
||||
*::-webkit-scrollbar { width: 6px; height: 6px; }
|
||||
*::-webkit-scrollbar-track { background: transparent; }
|
||||
*::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 3px; }
|
||||
.dark *::-webkit-scrollbar-thumb { background: #475569; }
|
||||
.toc-link.active, .toc-sublink.active { background: #eff6ff; color: #2563eb; }
|
||||
.dark .toc-link.active, .dark .toc-sublink.active { background: #1e3a5f; color: #60a5fa; }
|
||||
.page-section { animation: fadeIn 0.3s ease-in-out; }
|
||||
@keyframes fadeIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
|
||||
@media (max-width: 768px) { .sidebar { display: none !important; } .main-content { margin-left: 0 !important; padding-left: 1rem !important; padding-right: 1rem !important; } .mobile-toggle { display: flex !important; } }
|
||||
:root {
|
||||
--color-primary: #6366f1;
|
||||
--color-primary-hover: #4f46e5;
|
||||
--color-primary-light: #eef2ff;
|
||||
--color-success: #10b981;
|
||||
--color-warning: #f59e0b;
|
||||
--color-danger: #ef4444;
|
||||
--color-surface: #ffffff;
|
||||
--color-surface-elevated: #f8fafc;
|
||||
--color-background: #f1f5f9;
|
||||
--color-text-primary: #0f172a;
|
||||
--color-text-secondary: #475569;
|
||||
--color-text-tertiary: #94a3b8;
|
||||
--color-border: #e2e8f0;
|
||||
--radius-sm: 6px;
|
||||
--radius-md: 10px;
|
||||
--radius-lg: 16px;
|
||||
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
||||
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||||
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
||||
--font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
--font-mono: 'JetBrains Mono', 'Fira Code', Consolas, monospace;
|
||||
--transition-fast: 150ms ease-out;
|
||||
--transition-normal: 250ms ease-out;
|
||||
--spacing-unit: 4px;
|
||||
}
|
||||
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: 16px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--font-sans);
|
||||
background-color: var(--color-background);
|
||||
color: var(--color-text-primary);
|
||||
line-height: 1.6;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
background-color: var(--color-surface);
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
padding: 0 24px;
|
||||
height: 64px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--color-text-primary);
|
||||
text-decoration: none;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
.navbar-brand svg {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.navbar-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 13px;
|
||||
color: var(--color-text-tertiary);
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 32px 24px;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: var(--color-surface);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-md);
|
||||
border: 1px solid var(--color-border);
|
||||
overflow: hidden;
|
||||
transition: box-shadow var(--transition-normal), transform var(--transition-normal);
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
box-shadow: var(--shadow-lg);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
padding: 20px 24px;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
background-color: var(--color-surface-elevated);
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: var(--color-text-primary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.card-title svg {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.log-entry {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.log-entry:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.log-date {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: var(--color-text-tertiary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
margin-bottom: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.log-date::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: var(--color-primary);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
thead {
|
||||
background-color: var(--color-surface-elevated);
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
padding: 12px 16px;
|
||||
font-weight: 600;
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: var(--color-text-secondary);
|
||||
border-bottom: 2px solid var(--color-border);
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
color: var(--color-text-primary);
|
||||
transition: background-color var(--transition-fast);
|
||||
}
|
||||
|
||||
tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
tr:hover td {
|
||||
background-color: var(--color-primary-light);
|
||||
}
|
||||
|
||||
td:first-child, th:first-child {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
td:last-child, th:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.table-highlight {
|
||||
background-color: var(--color-primary-light) !important;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.table-highlight td {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 13px;
|
||||
background-color: var(--color-surface-elevated);
|
||||
padding: 2px 6px;
|
||||
border-radius: var(--radius-sm);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left: 4px solid var(--color-primary);
|
||||
padding-left: 16px;
|
||||
margin: 16px 0;
|
||||
color: var(--color-text-secondary);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-primary);
|
||||
text-decoration: none;
|
||||
transition: color var(--transition-fast);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--color-primary-hover);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 16px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
table {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 8px 10px;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.navbar-meta {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*, *::before, *::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-white dark:bg-gray-950 text-gray-900 dark:text-gray-100 font-sans antialiased transition-colors">
|
||||
<div class="flex min-h-screen">
|
||||
<!-- Sidebar -->
|
||||
<aside class="sidebar fixed left-0 top-0 h-screen w-64 bg-gray-50 dark:bg-gray-900 border-r border-gray-200 dark:border-gray-800 overflow-y-auto z-40 transition-transform">
|
||||
<div class="p-5 border-b border-gray-200 dark:border-gray-800">
|
||||
<h2 class="text-lg font-bold text-gray-900 dark:text-white flex items-center gap-2">
|
||||
<svg class="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/></svg>
|
||||
LLM Logs
|
||||
</h2>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">1 file(s)</p>
|
||||
<body>
|
||||
<nav class="navbar" role="navigation" aria-label="Main navigation">
|
||||
<a href="#" class="navbar-brand">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M12 2L2 7l10 5 10-5-10-5z"/>
|
||||
<path d="M2 17l10 5 10-5"/>
|
||||
<path d="M2 12l10 5 10-5"/>
|
||||
</svg>
|
||||
LLM Log Dashboard
|
||||
</a>
|
||||
<div class="navbar-meta">
|
||||
<span id="last-updated"></span>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="container" id="main-content">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h1 class="card-title">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
|
||||
<line x1="3" y1="9" x2="21" y2="9"/>
|
||||
<line x1="9" y1="21" x2="9" y2="9"/>
|
||||
</svg>
|
||||
日志记录
|
||||
</h1>
|
||||
</div>
|
||||
<nav class="p-3">
|
||||
<ul class="space-y-0.5">
|
||||
<li>
|
||||
<a href="#page-0" class="toc-link block px-3 py-2 text-sm rounded-md transition-colors hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white font-medium" data-target="0">
|
||||
2026-04-26
|
||||
|
||||
</a>
|
||||
<div class="card-body" id="log-content">
|
||||
|
||||
</li></ul>
|
||||
</nav>
|
||||
</aside>
|
||||
<section class="log-entry" aria-label="2026-04-26">
|
||||
<div class="log-date">2026-04-26</div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th align="center">小时</th>
|
||||
<th align="center">平均输入t/s</th>
|
||||
<th align="center">平均输出t/s</th>
|
||||
<th align="center">输入token总数</th>
|
||||
<th align="center">输出token总数</th>
|
||||
<th align="center">token总数</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody><tr>
|
||||
<td align="center">00</td>
|
||||
<td align="center">216.04</td>
|
||||
<td align="center">26.08</td>
|
||||
<td align="center">115,105</td>
|
||||
<td align="center">5,701</td>
|
||||
<td align="center">120,806</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">08</td>
|
||||
<td align="center">300.02</td>
|
||||
<td align="center">34.15</td>
|
||||
<td align="center">96,329</td>
|
||||
<td align="center">16,921</td>
|
||||
<td align="center">113,250</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">09</td>
|
||||
<td align="center">190.30</td>
|
||||
<td align="center">28.20</td>
|
||||
<td align="center">19,066</td>
|
||||
<td align="center">1,389</td>
|
||||
<td align="center">20,455</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">10</td>
|
||||
<td align="center">58.32</td>
|
||||
<td align="center">24.36</td>
|
||||
<td align="center">538</td>
|
||||
<td align="center">2,076</td>
|
||||
<td align="center">2,614</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">12</td>
|
||||
<td align="center">166.90</td>
|
||||
<td align="center">18.84</td>
|
||||
<td align="center">45,509</td>
|
||||
<td align="center">8,311</td>
|
||||
<td align="center">53,820</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">13</td>
|
||||
<td align="center">52.57</td>
|
||||
<td align="center">28.39</td>
|
||||
<td align="center">39</td>
|
||||
<td align="center">1,438</td>
|
||||
<td align="center">1,477</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">15</td>
|
||||
<td align="center">245.35</td>
|
||||
<td align="center">17.57</td>
|
||||
<td align="center">54,168</td>
|
||||
<td align="center">3,735</td>
|
||||
<td align="center">57,903</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">16</td>
|
||||
<td align="center">259.63</td>
|
||||
<td align="center">27.41</td>
|
||||
<td align="center">69,957</td>
|
||||
<td align="center">7,702</td>
|
||||
<td align="center">77,659</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">17</td>
|
||||
<td align="center">304.44</td>
|
||||
<td align="center">25.81</td>
|
||||
<td align="center">44,088</td>
|
||||
<td align="center">1,764</td>
|
||||
<td align="center">45,852</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">18</td>
|
||||
<td align="center">233.83</td>
|
||||
<td align="center">27.90</td>
|
||||
<td align="center">206,821</td>
|
||||
<td align="center">17,378</td>
|
||||
<td align="center">224,199</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">20</td>
|
||||
<td align="center">374.10</td>
|
||||
<td align="center">25.27</td>
|
||||
<td align="center">30,747</td>
|
||||
<td align="center">3,918</td>
|
||||
<td align="center">34,665</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">21</td>
|
||||
<td align="center">237.74</td>
|
||||
<td align="center">31.39</td>
|
||||
<td align="center">1,516</td>
|
||||
<td align="center">5,912</td>
|
||||
<td align="center">7,428</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">23</td>
|
||||
<td align="center">283.86</td>
|
||||
<td align="center">25.43</td>
|
||||
<td align="center">39,894</td>
|
||||
<td align="center">1,999</td>
|
||||
<td align="center">41,893</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><strong>全天</strong></td>
|
||||
<td align="center"><strong>233.31</strong></td>
|
||||
<td align="center"><strong>26.56</strong></td>
|
||||
<td align="center"><strong>723,777</strong></td>
|
||||
<td align="center"><strong>78,244</strong></td>
|
||||
<td align="center"><strong>802,021</strong></td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th align="left">厂商</th>
|
||||
<th align="left">模型</th>
|
||||
<th align="center">输入费用 (元)</th>
|
||||
<th align="center">输出费用 (元)</th>
|
||||
<th align="center">总费用 (元)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody><tr>
|
||||
<td align="left">🚀 DeepSeek</td>
|
||||
<td align="left">DeepSeek-V4-Flash</td>
|
||||
<td align="center">0.72</td>
|
||||
<td align="center">0.16</td>
|
||||
<td align="center"><strong>0.88</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left">🚀 DeepSeek</td>
|
||||
<td align="left">DeepSeek-V4-Pro</td>
|
||||
<td align="center">8.69</td>
|
||||
<td align="center">1.88</td>
|
||||
<td align="center"><strong>10.57</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left">☁️ 千问 (Qwen)</td>
|
||||
<td align="left">Qwen3.6-Plus</td>
|
||||
<td align="center">1.45</td>
|
||||
<td align="center">0.94</td>
|
||||
<td align="center"><strong>2.39</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left">🌐 MiniMax</td>
|
||||
<td align="left">MiniMax-M2.7</td>
|
||||
<td align="center">1.52</td>
|
||||
<td align="center">0.66</td>
|
||||
<td align="center"><strong>2.18</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left">🌐 MiniMax</td>
|
||||
<td align="left">MiniMax-M2.7-highspeed</td>
|
||||
<td align="center">3.04</td>
|
||||
<td align="center">1.31</td>
|
||||
<td align="center"><strong>4.35</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left">🤖 智谱 (GLM)</td>
|
||||
<td align="left">GLM-5.1</td>
|
||||
<td align="center">4.34</td>
|
||||
<td align="center">1.88</td>
|
||||
<td align="center"><strong>6.22</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left">🏠 小米 (MiMo)</td>
|
||||
<td align="left">MiMo-V2-Pro (256K 档)</td>
|
||||
<td align="center">2.03</td>
|
||||
<td align="center">1.10</td>
|
||||
<td align="center"><strong>3.13</strong></td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="main-content flex-1 ml-64 min-w-0">
|
||||
<header class="sticky top-0 z-30 bg-white/80 dark:bg-gray-950/80 backdrop-blur-md border-b border-gray-200 dark:border-gray-800">
|
||||
<div class="flex items-center justify-between px-6 py-3">
|
||||
<button id="menuBtn" class="mobile-toggle hidden items-center p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/></svg>
|
||||
</button>
|
||||
<div class="flex items-center gap-3 ml-auto">
|
||||
<button id="themeToggle" class="p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors" title="Toggle theme">
|
||||
<svg id="sunIcon" class="w-5 h-5 hidden dark:block" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"/></svg>
|
||||
<svg id="moonIcon" class="w-5 h-5 block dark:hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="max-w-4xl mx-auto px-6 py-10">
|
||||
|
||||
<section id="page-0" class="page-section mb-20 scroll-mt-24">
|
||||
<div class="mb-8 pb-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h1 class="text-3xl font-bold text-gray-900 dark:text-white mb-2">2026-04-26</h1>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">2026-04-26.md</p>
|
||||
</div>
|
||||
<div class="prose prose-gray dark:prose-invert max-w-none prose-headings:font-bold prose-headings:text-gray-900 dark:prose-headings:text-white prose-p:text-gray-700 dark:prose-p:text-gray-300 prose-a:text-blue-600 dark:prose-a:text-blue-400 prose-strong:text-gray-900 dark:prose-strong:text-white prose-code:text-sm prose-pre:rounded-lg prose-pre:bg-gray-900 prose-img:rounded-lg prose-table:border-collapse prose-table:border prose-table:border-gray-200 dark:prose-table:border-gray-700 prose-th:bg-gray-50 dark:prose-th:bg-gray-800 prose-th:border prose-th:border-gray-200 dark:prose-th:border-gray-700 prose-td:border prose-td:border-gray-200 dark:prose-td:border-gray-700 prose-li:marker:text-gray-500"><div class="overflow-x-auto my-6 rounded-lg border border-gray-200 dark:border-gray-700 shadow-sm"><table class="w-full text-sm"><thead><tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><th class="px-4 py-3 text-left font-semibold bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700" align="center">小时</th>
|
||||
<th class="px-4 py-3 text-left font-semibold bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700" align="center">平均输入t/s</th>
|
||||
<th class="px-4 py-3 text-left font-semibold bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700" align="center">平均输出t/s</th>
|
||||
<th class="px-4 py-3 text-left font-semibold bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700" align="center">输入token总数</th>
|
||||
<th class="px-4 py-3 text-left font-semibold bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700" align="center">输出token总数</th>
|
||||
<th class="px-4 py-3 text-left font-semibold bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700" align="center">token总数</th>
|
||||
</tr>
|
||||
</thead><tbody><tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">00</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">216.04</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">26.08</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">115,105</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">5,701</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">120,806</td>
|
||||
</tr>
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">08</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">300.02</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">34.15</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">96,329</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">16,921</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">113,250</td>
|
||||
</tr>
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">09</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">190.30</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">28.20</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">19,066</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">1,389</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">20,455</td>
|
||||
</tr>
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">10</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">58.32</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">24.36</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">538</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">2,076</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">2,614</td>
|
||||
</tr>
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">12</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">166.90</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">18.84</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">45,509</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">8,311</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">53,820</td>
|
||||
</tr>
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">13</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">52.57</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">28.39</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">39</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">1,438</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">1,477</td>
|
||||
</tr>
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">15</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">245.35</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">17.57</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">54,168</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">3,735</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">57,903</td>
|
||||
</tr>
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">16</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">259.63</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">27.41</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">69,957</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">7,702</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">77,659</td>
|
||||
</tr>
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">17</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">304.44</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">25.81</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">44,088</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">1,764</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">45,852</td>
|
||||
</tr>
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">18</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">233.83</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">27.90</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">206,821</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">17,378</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">224,199</td>
|
||||
</tr>
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">20</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">374.10</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">25.27</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">30,747</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">3,918</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">34,665</td>
|
||||
</tr>
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">21</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">237.74</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">31.39</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">1,516</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">5,912</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">7,428</td>
|
||||
</tr>
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">23</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">283.86</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">25.43</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">39,894</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">1,999</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">41,893</td>
|
||||
</tr>
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center"><strong>全天</strong></td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center"><strong>233.31</strong></td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center"><strong>26.56</strong></td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center"><strong>723,777</strong></td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center"><strong>78,244</strong></td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center"><strong>802,021</strong></td>
|
||||
</tr>
|
||||
</tbody></table></div>
|
||||
<div class="overflow-x-auto my-6 rounded-lg border border-gray-200 dark:border-gray-700 shadow-sm"><table class="w-full text-sm"><thead><tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><th class="px-4 py-3 text-left font-semibold bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700" align="left">厂商</th>
|
||||
<th class="px-4 py-3 text-left font-semibold bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700" align="left">模型</th>
|
||||
<th class="px-4 py-3 text-left font-semibold bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700" align="center">输入费用 (元)</th>
|
||||
<th class="px-4 py-3 text-left font-semibold bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700" align="center">输出费用 (元)</th>
|
||||
<th class="px-4 py-3 text-left font-semibold bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700" align="center">总费用 (元)</th>
|
||||
</tr>
|
||||
</thead><tbody><tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="left">🚀 DeepSeek</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="left">DeepSeek-V4-Flash</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">0.72</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">0.16</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center"><strong>0.88</strong></td>
|
||||
</tr>
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="left">🚀 DeepSeek</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="left">DeepSeek-V4-Pro</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">8.69</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">1.88</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center"><strong>10.57</strong></td>
|
||||
</tr>
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="left">☁️ 千问 (Qwen)</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="left">Qwen3.6-Plus</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">1.45</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">0.94</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center"><strong>2.39</strong></td>
|
||||
</tr>
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="left">🌐 MiniMax</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="left">MiniMax-M2.7</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">1.52</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">0.66</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center"><strong>2.18</strong></td>
|
||||
</tr>
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="left">🌐 MiniMax</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="left">MiniMax-M2.7-highspeed</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">3.04</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">1.31</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center"><strong>4.35</strong></td>
|
||||
</tr>
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="left">🤖 智谱 (GLM)</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="left">GLM-5.1</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">4.34</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">1.88</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center"><strong>6.22</strong></td>
|
||||
</tr>
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="left">🏠 小米 (MiMo)</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="left">MiMo-V2-Pro (256K 档)</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">2.03</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">1.10</td>
|
||||
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center"><strong>3.13</strong></td>
|
||||
</tr>
|
||||
</tbody></table></div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
|
||||
<section class="log-entry" aria-label="2026-04-26 - 副本">
|
||||
<div class="log-date">2026-04-26 - 副本</div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th align="center">小时</th>
|
||||
<th align="center">平均输入t/s</th>
|
||||
<th align="center">平均输出t/s</th>
|
||||
<th align="center">输入token总数</th>
|
||||
<th align="center">输出token总数</th>
|
||||
<th align="center">token总数</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody><tr>
|
||||
<td align="center">00</td>
|
||||
<td align="center">216.04</td>
|
||||
<td align="center">26.08</td>
|
||||
<td align="center">115,105</td>
|
||||
<td align="center">5,701</td>
|
||||
<td align="center">120,806</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">08</td>
|
||||
<td align="center">300.02</td>
|
||||
<td align="center">34.15</td>
|
||||
<td align="center">96,329</td>
|
||||
<td align="center">16,921</td>
|
||||
<td align="center">113,250</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">09</td>
|
||||
<td align="center">190.30</td>
|
||||
<td align="center">28.20</td>
|
||||
<td align="center">19,066</td>
|
||||
<td align="center">1,389</td>
|
||||
<td align="center">20,455</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">10</td>
|
||||
<td align="center">58.32</td>
|
||||
<td align="center">24.36</td>
|
||||
<td align="center">538</td>
|
||||
<td align="center">2,076</td>
|
||||
<td align="center">2,614</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">12</td>
|
||||
<td align="center">166.90</td>
|
||||
<td align="center">18.84</td>
|
||||
<td align="center">45,509</td>
|
||||
<td align="center">8,311</td>
|
||||
<td align="center">53,820</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">13</td>
|
||||
<td align="center">52.57</td>
|
||||
<td align="center">28.39</td>
|
||||
<td align="center">39</td>
|
||||
<td align="center">1,438</td>
|
||||
<td align="center">1,477</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">15</td>
|
||||
<td align="center">245.35</td>
|
||||
<td align="center">17.57</td>
|
||||
<td align="center">54,168</td>
|
||||
<td align="center">3,735</td>
|
||||
<td align="center">57,903</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">16</td>
|
||||
<td align="center">259.63</td>
|
||||
<td align="center">27.41</td>
|
||||
<td align="center">69,957</td>
|
||||
<td align="center">7,702</td>
|
||||
<td align="center">77,659</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">17</td>
|
||||
<td align="center">304.44</td>
|
||||
<td align="center">25.81</td>
|
||||
<td align="center">44,088</td>
|
||||
<td align="center">1,764</td>
|
||||
<td align="center">45,852</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">18</td>
|
||||
<td align="center">233.83</td>
|
||||
<td align="center">27.90</td>
|
||||
<td align="center">206,821</td>
|
||||
<td align="center">17,378</td>
|
||||
<td align="center">224,199</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">20</td>
|
||||
<td align="center">374.10</td>
|
||||
<td align="center">25.27</td>
|
||||
<td align="center">30,747</td>
|
||||
<td align="center">3,918</td>
|
||||
<td align="center">34,665</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">21</td>
|
||||
<td align="center">237.74</td>
|
||||
<td align="center">31.39</td>
|
||||
<td align="center">1,516</td>
|
||||
<td align="center">5,912</td>
|
||||
<td align="center">7,428</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">23</td>
|
||||
<td align="center">283.86</td>
|
||||
<td align="center">25.43</td>
|
||||
<td align="center">39,894</td>
|
||||
<td align="center">1,999</td>
|
||||
<td align="center">41,893</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><strong>全天</strong></td>
|
||||
<td align="center"><strong>233.31</strong></td>
|
||||
<td align="center"><strong>26.56</strong></td>
|
||||
<td align="center"><strong>723,777</strong></td>
|
||||
<td align="center"><strong>78,244</strong></td>
|
||||
<td align="center"><strong>802,021</strong></td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th align="left">厂商</th>
|
||||
<th align="left">模型</th>
|
||||
<th align="center">输入费用 (元)</th>
|
||||
<th align="center">输出费用 (元)</th>
|
||||
<th align="center">总费用 (元)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody><tr>
|
||||
<td align="left">🚀 DeepSeek</td>
|
||||
<td align="left">DeepSeek-V4-Flash</td>
|
||||
<td align="center">0.72</td>
|
||||
<td align="center">0.16</td>
|
||||
<td align="center"><strong>0.88</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left">🚀 DeepSeek</td>
|
||||
<td align="left">DeepSeek-V4-Pro</td>
|
||||
<td align="center">8.69</td>
|
||||
<td align="center">1.88</td>
|
||||
<td align="center"><strong>10.57</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left">☁️ 千问 (Qwen)</td>
|
||||
<td align="left">Qwen3.6-Plus</td>
|
||||
<td align="center">1.45</td>
|
||||
<td align="center">0.94</td>
|
||||
<td align="center"><strong>2.39</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left">🌐 MiniMax</td>
|
||||
<td align="left">MiniMax-M2.7</td>
|
||||
<td align="center">1.52</td>
|
||||
<td align="center">0.66</td>
|
||||
<td align="center"><strong>2.18</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left">🌐 MiniMax</td>
|
||||
<td align="left">MiniMax-M2.7-highspeed</td>
|
||||
<td align="center">3.04</td>
|
||||
<td align="center">1.31</td>
|
||||
<td align="center"><strong>4.35</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left">🤖 智谱 (GLM)</td>
|
||||
<td align="left">GLM-5.1</td>
|
||||
<td align="center">4.34</td>
|
||||
<td align="center">1.88</td>
|
||||
<td align="center"><strong>6.22</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left">🏠 小米 (MiMo)</td>
|
||||
<td align="left">MiMo-V2-Pro (256K 档)</td>
|
||||
<td align="center">2.03</td>
|
||||
<td align="center">1.10</td>
|
||||
<td align="center"><strong>3.13</strong></td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Scripts -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/bash.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/javascript.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/typescript.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/python.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/json.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/css.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/xml.min.js"></script>
|
||||
<script>
|
||||
// Init highlight.js
|
||||
hljs.highlightAll();
|
||||
|
||||
// Theme toggle
|
||||
const html = document.documentElement;
|
||||
const themeBtn = document.getElementById('themeToggle');
|
||||
const saved = localStorage.getItem('theme');
|
||||
if (saved === 'dark' || (!saved && window.matchMedia('(prefers-color-scheme: dark)').matches)) html.classList.add('dark');
|
||||
themeBtn.addEventListener('click', () => {
|
||||
html.classList.toggle('dark');
|
||||
localStorage.setItem('theme', html.classList.contains('dark') ? 'dark' : 'light');
|
||||
document.getElementById('hljs-light').disabled = html.classList.contains('dark');
|
||||
document.getElementById('hljs-dark').disabled = !html.classList.contains('dark');
|
||||
});
|
||||
|
||||
// Sidebar toggle (mobile)
|
||||
document.getElementById('menuBtn').addEventListener('click', () => {
|
||||
document.querySelector('.sidebar').classList.toggle('-translate-x-full');
|
||||
});
|
||||
|
||||
// Active TOC tracking
|
||||
const sections = document.querySelectorAll('.page-section');
|
||||
const tocLinks = document.querySelectorAll('.toc-link, .toc-sublink');
|
||||
const observer = new IntersectionObserver(entries => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
const idx = entry.target.id.replace('page-', '');
|
||||
tocLinks.forEach(l => l.classList.toggle('active', l.dataset.target === idx));
|
||||
}
|
||||
});
|
||||
}, { rootMargin: '-80px 0px -70% 0px', threshold: 0 });
|
||||
sections.forEach(s => observer.observe(s));
|
||||
|
||||
// Smooth scroll for TOC links
|
||||
tocLinks.forEach(link => {
|
||||
link.addEventListener('click', e => {
|
||||
e.preventDefault();
|
||||
const targetId = link.href.split('#').pop();
|
||||
document.getElementById(targetId)?.scrollIntoView({ behavior: 'smooth' });
|
||||
// Close mobile sidebar
|
||||
if (window.innerWidth < 768) document.querySelector('.sidebar').classList.add('-translate-x-full');
|
||||
});
|
||||
});
|
||||
|
||||
// Copy code button
|
||||
window.copyCode = function(btn) {
|
||||
const code = btn.nextElementSibling;
|
||||
navigator.clipboard.writeText(code.textContent).then(() => {
|
||||
btn.textContent = 'Copied!';
|
||||
setTimeout(() => btn.textContent = 'Copy', 2000);
|
||||
});
|
||||
};
|
||||
document.getElementById('last-updated').textContent = new Date().toLocaleString('zh-CN');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
17
package-lock.json
generated
17
package-lock.json
generated
@ -1,25 +1,14 @@
|
||||
{
|
||||
"name": "llm_log_frontend",
|
||||
"name": "llm-log-frontend",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "llm_log_frontend",
|
||||
"name": "llm-log-frontend",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"highlight.js": "^11.11.1",
|
||||
"marked": "^12.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/highlight.js": {
|
||||
"version": "11.11.1",
|
||||
"resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-11.11.1.tgz",
|
||||
"integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
"marked": "^12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/marked": {
|
||||
|
||||
17
package.json
17
package.json
@ -1,17 +1,12 @@
|
||||
{
|
||||
"name": "llm_log_frontend",
|
||||
"name": "llm-log-frontend",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"description": "LLM Log Frontend - Aggregated static HTML generator",
|
||||
"main": "generate.js",
|
||||
"scripts": {
|
||||
"build": "node build.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
"generate": "node generate.js"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"highlight.js": "^11.11.1",
|
||||
"marked": "^12.0.2"
|
||||
"marked": "^12.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user