agent-reader 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +213 -0
- package/bin/agent-reader.js +83 -0
- package/package.json +52 -0
- package/src/cli/commands.js +602 -0
- package/src/core/assets.js +429 -0
- package/src/core/exporter.js +710 -0
- package/src/core/opener.js +329 -0
- package/src/core/renderer.js +235 -0
- package/src/core/sanitizer.js +79 -0
- package/src/core/slideshow.js +383 -0
- package/src/core/templates/docx-table.lua +4 -0
- package/src/core/templates/reference.docx +0 -0
- package/src/core/themes/dark.css +256 -0
- package/src/core/themes/light.css +312 -0
- package/src/core/themes/print.css +54 -0
- package/src/mcp/server.js +381 -0
- package/src/templates/document.html +145 -0
- package/src/templates/slideshow.html +42 -0
- package/src/utils/logger.js +64 -0
- package/src/utils/naturalSort.js +12 -0
- package/src/utils/output.js +85 -0
- package/src/utils/preferences.js +89 -0
- package/src/utils/server.js +295 -0
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
|
+
import { promises as fs } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { createRequire } from 'node:module';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { processAssets } from './assets.js';
|
|
7
|
+
import { naturalCompare } from '../utils/naturalSort.js';
|
|
8
|
+
|
|
9
|
+
const require = createRequire(import.meta.url);
|
|
10
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
const TEMPLATE_PATH = path.resolve(__dirname, '../templates/slideshow.html');
|
|
12
|
+
const SWIPER_ROOT = path.dirname(require.resolve('swiper/package.json'));
|
|
13
|
+
const SWIPER_CSS_PATH = path.join(SWIPER_ROOT, 'swiper-bundle.min.css');
|
|
14
|
+
const SWIPER_JS_PATH = path.join(SWIPER_ROOT, 'swiper-bundle.min.js');
|
|
15
|
+
|
|
16
|
+
const IMAGE_EXTENSIONS = new Set(['.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp']);
|
|
17
|
+
|
|
18
|
+
function isImageFile(fileName) {
|
|
19
|
+
return IMAGE_EXTENSIONS.has(path.extname(fileName).toLowerCase());
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function escapeHtml(value) {
|
|
23
|
+
return String(value)
|
|
24
|
+
.replaceAll('&', '&')
|
|
25
|
+
.replaceAll('<', '<')
|
|
26
|
+
.replaceAll('>', '>')
|
|
27
|
+
.replaceAll('"', '"')
|
|
28
|
+
.replaceAll("'", ''');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function slideLabelFromFileName(fileName) {
|
|
32
|
+
const raw = path.basename(fileName, path.extname(fileName));
|
|
33
|
+
return raw.replace(/[_-]+/g, ' ').trim() || fileName;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function buildSlideHtml(fileName, slideNumber) {
|
|
37
|
+
return `<div class="swiper-slide" data-slide-number="${slideNumber}"><img src="./${fileName}" alt="${fileName}" loading="lazy" decoding="async"></div>`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function buildThumbHtml(fileName) {
|
|
41
|
+
return `<div class="swiper-slide"><img src="./${fileName}" alt="${fileName}" loading="lazy" decoding="async"></div>`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function buildTocItemHtml(fileName, index) {
|
|
45
|
+
const label = escapeHtml(slideLabelFromFileName(fileName));
|
|
46
|
+
return `<button class="slide-toc-item" type="button" data-slide-index="${index}"><span class="slide-toc-index">${index + 1}</span><span class="slide-toc-label">${label}</span></button>`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function extractBetween(markup, id) {
|
|
50
|
+
const regex = new RegExp(`<section data-id=\"${id}\">([\\s\\S]*?)<\\/section>`);
|
|
51
|
+
const result = markup.match(regex);
|
|
52
|
+
return result ? result[1] : '';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export async function createSlideshow(imageDir, options = {}) {
|
|
56
|
+
const {
|
|
57
|
+
autoPlay,
|
|
58
|
+
inlineAll = false,
|
|
59
|
+
outDir,
|
|
60
|
+
theme = 'light',
|
|
61
|
+
} = options;
|
|
62
|
+
|
|
63
|
+
void theme;
|
|
64
|
+
|
|
65
|
+
const dirPath = path.resolve(imageDir);
|
|
66
|
+
const dirEntries = await fs.readdir(dirPath, { withFileTypes: true });
|
|
67
|
+
const files = dirEntries
|
|
68
|
+
.filter((entry) => entry.isFile())
|
|
69
|
+
.map((entry) => entry.name)
|
|
70
|
+
.filter(isImageFile)
|
|
71
|
+
.sort(naturalCompare);
|
|
72
|
+
|
|
73
|
+
const warnings = [];
|
|
74
|
+
if (files.length === 0) {
|
|
75
|
+
warnings.push(`no images found in ${dirPath}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const slides = files.map((fileName, index) => buildSlideHtml(fileName, index + 1)).join('\n');
|
|
79
|
+
const thumbs = files.map(buildThumbHtml).join('\n');
|
|
80
|
+
const tocItems = files.map(buildTocItemHtml).join('\n');
|
|
81
|
+
|
|
82
|
+
const wrapped = `<section data-id="slides">${slides}</section><section data-id="thumbs">${thumbs}</section>`;
|
|
83
|
+
const assetResult = await processAssets(wrapped, {
|
|
84
|
+
baseDir: dirPath,
|
|
85
|
+
outDir,
|
|
86
|
+
inlineAll,
|
|
87
|
+
fetchRemote: false,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const processedSlides = extractBetween(assetResult.html, 'slides');
|
|
91
|
+
const processedThumbs = extractBetween(assetResult.html, 'thumbs');
|
|
92
|
+
|
|
93
|
+
warnings.push(...assetResult.warnings);
|
|
94
|
+
|
|
95
|
+
const [template, swiperCss, swiperJs] = await Promise.all([
|
|
96
|
+
fs.readFile(TEMPLATE_PATH, 'utf8'),
|
|
97
|
+
fs.readFile(SWIPER_CSS_PATH, 'utf8'),
|
|
98
|
+
fs.readFile(SWIPER_JS_PATH, 'utf8'),
|
|
99
|
+
]);
|
|
100
|
+
|
|
101
|
+
const customCss = `
|
|
102
|
+
${swiperCss}
|
|
103
|
+
html, body {
|
|
104
|
+
margin: 0;
|
|
105
|
+
background: #090e1a;
|
|
106
|
+
color: #fff;
|
|
107
|
+
font-family: -apple-system, BlinkMacSystemFont, "Noto Sans SC", "Microsoft YaHei", "PingFang SC", sans-serif;
|
|
108
|
+
}
|
|
109
|
+
.shell { min-height: 100vh; display: flex; flex-direction: column; }
|
|
110
|
+
.toolbar { position: fixed; top: 14px; right: 16px; z-index: 110; display: flex; gap: 8px; }
|
|
111
|
+
.toolbar button {
|
|
112
|
+
border: 1px solid rgba(255,255,255,0.1);
|
|
113
|
+
border-radius: 999px;
|
|
114
|
+
padding: 7px 14px;
|
|
115
|
+
cursor: pointer;
|
|
116
|
+
background: rgba(30,30,30,0.6);
|
|
117
|
+
backdrop-filter: blur(10px);
|
|
118
|
+
-webkit-backdrop-filter: blur(10px);
|
|
119
|
+
color: #fff;
|
|
120
|
+
font-size: 13px;
|
|
121
|
+
display: flex;
|
|
122
|
+
align-items: center;
|
|
123
|
+
gap: 5px;
|
|
124
|
+
transition: background 0.2s ease;
|
|
125
|
+
}
|
|
126
|
+
.toolbar button:hover { background: rgba(60,60,60,0.7); }
|
|
127
|
+
.toolbar button svg { flex-shrink: 0; }
|
|
128
|
+
.slide-toc-drawer {
|
|
129
|
+
position: fixed;
|
|
130
|
+
top: 0;
|
|
131
|
+
left: 0;
|
|
132
|
+
width: min(360px, 85vw);
|
|
133
|
+
height: 100vh;
|
|
134
|
+
background: rgba(5, 9, 20, 0.96);
|
|
135
|
+
border-right: 1px solid rgba(148, 163, 184, 0.32);
|
|
136
|
+
transform: translateX(-100%);
|
|
137
|
+
transition: transform 0.24s ease;
|
|
138
|
+
z-index: 105;
|
|
139
|
+
padding: 16px;
|
|
140
|
+
box-sizing: border-box;
|
|
141
|
+
overflow: auto;
|
|
142
|
+
}
|
|
143
|
+
.slide-toc-drawer.is-open { transform: translateX(0); }
|
|
144
|
+
.slide-toc-title { margin: 0 0 12px; font-size: 16px; }
|
|
145
|
+
.slide-toc-list { display: flex; flex-direction: column; gap: 6px; }
|
|
146
|
+
.slide-toc-item {
|
|
147
|
+
border: 1px solid rgba(148, 163, 184, 0.3);
|
|
148
|
+
background: rgba(15, 23, 42, 0.6);
|
|
149
|
+
color: inherit;
|
|
150
|
+
text-align: left;
|
|
151
|
+
border-radius: 8px;
|
|
152
|
+
padding: 8px 10px;
|
|
153
|
+
display: flex;
|
|
154
|
+
gap: 10px;
|
|
155
|
+
align-items: center;
|
|
156
|
+
cursor: pointer;
|
|
157
|
+
}
|
|
158
|
+
.slide-toc-item:hover { background: rgba(30, 41, 59, 0.8); }
|
|
159
|
+
.slide-toc-item.is-active {
|
|
160
|
+
border-color: rgba(56, 189, 248, 0.85);
|
|
161
|
+
background: rgba(14, 116, 144, 0.35);
|
|
162
|
+
}
|
|
163
|
+
.slide-toc-index {
|
|
164
|
+
width: 24px;
|
|
165
|
+
text-align: center;
|
|
166
|
+
font-weight: 700;
|
|
167
|
+
font-size: 12px;
|
|
168
|
+
border-radius: 999px;
|
|
169
|
+
background: rgba(148, 163, 184, 0.3);
|
|
170
|
+
padding: 2px 0;
|
|
171
|
+
}
|
|
172
|
+
.slide-toc-label {
|
|
173
|
+
overflow: hidden;
|
|
174
|
+
text-overflow: ellipsis;
|
|
175
|
+
white-space: nowrap;
|
|
176
|
+
}
|
|
177
|
+
.main-swiper { flex: 1; width: 100vw; }
|
|
178
|
+
.main-swiper .swiper-slide { display: flex; align-items: center; justify-content: center; background: #04070e; }
|
|
179
|
+
.main-swiper img { max-width: 100%; max-height: calc(100vh - 120px); object-fit: contain; }
|
|
180
|
+
.main-swiper .swiper-button-prev,
|
|
181
|
+
.main-swiper .swiper-button-next {
|
|
182
|
+
width: 44px; height: 44px;
|
|
183
|
+
background: rgba(0,0,0,0.5);
|
|
184
|
+
border-radius: 50%;
|
|
185
|
+
transition: background 0.2s ease, transform 0.2s ease;
|
|
186
|
+
}
|
|
187
|
+
.main-swiper .swiper-button-prev:hover,
|
|
188
|
+
.main-swiper .swiper-button-next:hover {
|
|
189
|
+
background: rgba(0,0,0,0.8);
|
|
190
|
+
transform: scale(1.1);
|
|
191
|
+
}
|
|
192
|
+
.main-swiper .swiper-button-prev::after,
|
|
193
|
+
.main-swiper .swiper-button-next::after {
|
|
194
|
+
font-size: 18px;
|
|
195
|
+
color: #fff;
|
|
196
|
+
}
|
|
197
|
+
.thumb-swiper { height: 112px; background: rgba(0,0,0,0.4); overflow: hidden; }
|
|
198
|
+
.thumb-swiper .swiper-wrapper { scrollbar-width: none; }
|
|
199
|
+
.thumb-swiper .swiper-wrapper::-webkit-scrollbar { display: none; }
|
|
200
|
+
.thumb-swiper .swiper-slide {
|
|
201
|
+
width: 132px;
|
|
202
|
+
opacity: 0.45;
|
|
203
|
+
border: 2px solid transparent;
|
|
204
|
+
border-radius: 6px;
|
|
205
|
+
overflow: hidden;
|
|
206
|
+
box-sizing: border-box;
|
|
207
|
+
transition: opacity 0.2s ease, border-color 0.2s ease;
|
|
208
|
+
}
|
|
209
|
+
.thumb-swiper .swiper-slide:hover { opacity: 0.8; }
|
|
210
|
+
.thumb-swiper .swiper-slide-thumb-active {
|
|
211
|
+
opacity: 1;
|
|
212
|
+
border-color: #3B82F6;
|
|
213
|
+
box-shadow: 0 0 8px rgba(59,130,246,0.4);
|
|
214
|
+
}
|
|
215
|
+
.thumb-swiper img { width: 100%; height: 100%; object-fit: cover; }
|
|
216
|
+
:fullscreen .shell { height: 100vh; }
|
|
217
|
+
:fullscreen .thumb-swiper { display: none; }
|
|
218
|
+
:fullscreen .toolbar { display: none; }
|
|
219
|
+
:fullscreen .main-swiper { flex: 1; height: 100vh; }
|
|
220
|
+
:fullscreen .main-swiper .swiper-slide { height: 100vh; background: #000; }
|
|
221
|
+
:fullscreen .main-swiper img { max-height: 100vh; max-width: 100vw; }
|
|
222
|
+
@media print {
|
|
223
|
+
* { margin: 0; padding: 0; }
|
|
224
|
+
body { background: #fff; }
|
|
225
|
+
.toolbar, .thumb-swiper, .slide-toc-drawer,
|
|
226
|
+
.swiper-button-prev, .swiper-button-next { display: none !important; }
|
|
227
|
+
.shell { display: block; min-height: auto; }
|
|
228
|
+
.main-swiper { overflow: visible !important; height: auto !important; }
|
|
229
|
+
.main-swiper .swiper-wrapper { display: block !important; transform: none !important; }
|
|
230
|
+
.main-swiper .swiper-slide {
|
|
231
|
+
display: flex !important;
|
|
232
|
+
align-items: center;
|
|
233
|
+
justify-content: center;
|
|
234
|
+
width: 100vw !important;
|
|
235
|
+
height: 100vh !important;
|
|
236
|
+
page-break-after: always;
|
|
237
|
+
break-after: page;
|
|
238
|
+
background: #fff !important;
|
|
239
|
+
}
|
|
240
|
+
.main-swiper .swiper-slide:last-child { page-break-after: auto; break-after: auto; }
|
|
241
|
+
.main-swiper img { max-width: 90vw; max-height: 90vh; object-fit: contain; }
|
|
242
|
+
}
|
|
243
|
+
`;
|
|
244
|
+
|
|
245
|
+
const nonce = randomBytes(16).toString('base64url');
|
|
246
|
+
const autoplayConfig = Number(autoPlay) > 0
|
|
247
|
+
? `autoplay: { delay: ${Math.round(Number(autoPlay) * 1000)}, disableOnInteraction: false },`
|
|
248
|
+
: '';
|
|
249
|
+
|
|
250
|
+
const script = `
|
|
251
|
+
${swiperJs}
|
|
252
|
+
const tocDrawer = document.getElementById('slide-toc-drawer');
|
|
253
|
+
const tocToggleBtn = document.getElementById('toc-toggle-btn');
|
|
254
|
+
const tocItems = Array.from(document.querySelectorAll('.slide-toc-item'));
|
|
255
|
+
|
|
256
|
+
const thumbs = new Swiper('.thumb-swiper', {
|
|
257
|
+
spaceBetween: 8,
|
|
258
|
+
slidesPerView: 'auto',
|
|
259
|
+
watchSlidesProgress: true,
|
|
260
|
+
freeMode: true,
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
const main = new Swiper('.main-swiper', {
|
|
264
|
+
effect: 'fade',
|
|
265
|
+
fadeEffect: { crossFade: true },
|
|
266
|
+
keyboard: { enabled: true },
|
|
267
|
+
lazy: { loadPrevNext: true, loadPrevNextAmount: 2 },
|
|
268
|
+
preloadImages: false,
|
|
269
|
+
virtual: { addSlidesBefore: 2, addSlidesAfter: 2 },
|
|
270
|
+
navigation: {
|
|
271
|
+
nextEl: '.swiper-button-next',
|
|
272
|
+
prevEl: '.swiper-button-prev',
|
|
273
|
+
},
|
|
274
|
+
thumbs: { swiper: thumbs },
|
|
275
|
+
${autoplayConfig}
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
const setActiveToc = (index) => {
|
|
279
|
+
tocItems.forEach((item) => {
|
|
280
|
+
item.classList.toggle('is-active', Number(item.dataset.slideIndex) === index);
|
|
281
|
+
});
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
setActiveToc(0);
|
|
285
|
+
main.on('slideChange', () => {
|
|
286
|
+
const activeIndex = Number.isInteger(main.realIndex) ? main.realIndex : main.activeIndex;
|
|
287
|
+
setActiveToc(activeIndex || 0);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
tocItems.forEach((item) => {
|
|
291
|
+
item.addEventListener('click', () => {
|
|
292
|
+
const targetIndex = Number(item.dataset.slideIndex || 0);
|
|
293
|
+
main.slideTo(targetIndex);
|
|
294
|
+
tocDrawer?.classList.remove('is-open');
|
|
295
|
+
});
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
tocToggleBtn?.addEventListener('click', () => {
|
|
299
|
+
tocDrawer?.classList.toggle('is-open');
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
const fsBtn = document.getElementById('fullscreen-btn');
|
|
303
|
+
fsBtn?.addEventListener('click', async () => {
|
|
304
|
+
if (!document.fullscreenElement) {
|
|
305
|
+
await document.documentElement.requestFullscreen();
|
|
306
|
+
} else {
|
|
307
|
+
await document.exitFullscreen();
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
const exportBtn = document.getElementById('export-btn');
|
|
312
|
+
exportBtn?.addEventListener('click', async () => {
|
|
313
|
+
exportBtn.disabled = true;
|
|
314
|
+
exportBtn.textContent = '生成中…';
|
|
315
|
+
try {
|
|
316
|
+
const res = await fetch('/api/export?format=pdf&source=/_print.html');
|
|
317
|
+
if (!res.ok) throw new Error('export failed');
|
|
318
|
+
const blob = await res.blob();
|
|
319
|
+
const url = URL.createObjectURL(blob);
|
|
320
|
+
const a = document.createElement('a');
|
|
321
|
+
a.href = url;
|
|
322
|
+
a.download = res.headers.get('Content-Disposition')?.match(/filename="(.+)"/)?.[1] || 'slideshow.pdf';
|
|
323
|
+
document.body.appendChild(a);
|
|
324
|
+
a.click();
|
|
325
|
+
a.remove();
|
|
326
|
+
URL.revokeObjectURL(url);
|
|
327
|
+
} catch (e) {
|
|
328
|
+
alert('导出失败: ' + e.message);
|
|
329
|
+
} finally {
|
|
330
|
+
exportBtn.disabled = false;
|
|
331
|
+
exportBtn.textContent = '导出';
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
`;
|
|
335
|
+
|
|
336
|
+
const csp = `default-src 'none'; script-src 'nonce-${nonce}'; style-src 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; base-uri 'none'; frame-ancestors 'none';`;
|
|
337
|
+
|
|
338
|
+
const html = template
|
|
339
|
+
.replaceAll('{{TITLE}}', 'Agent Reader Slideshow')
|
|
340
|
+
.replaceAll('{{CSP}}', csp)
|
|
341
|
+
.replaceAll('{{STYLES}}', customCss)
|
|
342
|
+
.replaceAll('{{TOC_ITEMS}}', tocItems || '<p>暂无可用目录</p>')
|
|
343
|
+
.replaceAll('{{SLIDES}}', processedSlides)
|
|
344
|
+
.replaceAll('{{THUMBS}}', processedThumbs)
|
|
345
|
+
.replaceAll('{{NONCE}}', nonce)
|
|
346
|
+
.replaceAll('{{SCRIPT}}', script);
|
|
347
|
+
|
|
348
|
+
const printSlides = files.map((f) =>
|
|
349
|
+
`<div class="page"><img src="./${f}"></div>`
|
|
350
|
+
).join('\n');
|
|
351
|
+
|
|
352
|
+
const printWrapped = `<section data-id="print">${printSlides}</section>`;
|
|
353
|
+
const printAssetResult = await processAssets(printWrapped, {
|
|
354
|
+
baseDir: dirPath,
|
|
355
|
+
outDir,
|
|
356
|
+
inlineAll,
|
|
357
|
+
fetchRemote: false,
|
|
358
|
+
});
|
|
359
|
+
const processedPrintSlides = extractBetween(printAssetResult.html, 'print');
|
|
360
|
+
|
|
361
|
+
const printHtml = `<!doctype html>
|
|
362
|
+
<html><head><meta charset="utf-8"><style>
|
|
363
|
+
@page { size: A4 landscape; margin: 0; }
|
|
364
|
+
* { margin: 0; padding: 0; }
|
|
365
|
+
body { background: #fff; }
|
|
366
|
+
.page {
|
|
367
|
+
width: 100vw; height: 100vh;
|
|
368
|
+
display: flex; align-items: center; justify-content: center;
|
|
369
|
+
page-break-after: always; break-after: page;
|
|
370
|
+
background: #fff;
|
|
371
|
+
}
|
|
372
|
+
.page:last-child { page-break-after: auto; break-after: auto; }
|
|
373
|
+
img { max-width: 95vw; max-height: 95vh; object-fit: contain; }
|
|
374
|
+
</style></head><body>${processedPrintSlides}</body></html>`;
|
|
375
|
+
|
|
376
|
+
return {
|
|
377
|
+
html,
|
|
378
|
+
printHtml,
|
|
379
|
+
imageCount: files.length,
|
|
380
|
+
assets: assetResult.assets,
|
|
381
|
+
warnings,
|
|
382
|
+
};
|
|
383
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
color-scheme: dark;
|
|
3
|
+
--bg: #0a1323;
|
|
4
|
+
--text: #dbe5f3;
|
|
5
|
+
--text-muted: #afbdd3;
|
|
6
|
+
--card: #0f1d33;
|
|
7
|
+
--border: #263852;
|
|
8
|
+
--heading: #f5f8ff;
|
|
9
|
+
--link: #88c7ff;
|
|
10
|
+
--link-hover: #b6dcff;
|
|
11
|
+
--code-bg: #1c2f4c;
|
|
12
|
+
--code-text: #dbe7ff;
|
|
13
|
+
--blockquote-bg: rgba(33, 50, 76, 0.68);
|
|
14
|
+
--blockquote-border: #5f84bd;
|
|
15
|
+
--table-head: #1d2d46;
|
|
16
|
+
--table-stripe: rgba(255, 255, 255, 0.02);
|
|
17
|
+
--toc-card-bg: rgba(14, 27, 46, 0.92);
|
|
18
|
+
--toc-border: #2a3f5f;
|
|
19
|
+
--toc-shadow: 0 26px 50px -34px rgba(1, 6, 16, 0.88);
|
|
20
|
+
--toc-hover-bg: rgba(136, 199, 255, 0.13);
|
|
21
|
+
--toc-active-bg: rgba(136, 199, 255, 0.2);
|
|
22
|
+
--toc-active-border: #7ec2ff;
|
|
23
|
+
--toc-active-text: #cde8ff;
|
|
24
|
+
--toc-header-text: #f2f7ff;
|
|
25
|
+
--toc-muted: rgba(210, 226, 248, 0.72);
|
|
26
|
+
--toc-toggle-bg: rgba(11, 22, 38, 0.84);
|
|
27
|
+
--toc-toggle-hover-bg: rgba(16, 33, 56, 0.95);
|
|
28
|
+
--toc-toggle-border: #324966;
|
|
29
|
+
--toc-toggle-text: #d6e5fa;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
html,
|
|
33
|
+
body {
|
|
34
|
+
margin: 0;
|
|
35
|
+
padding: 0;
|
|
36
|
+
min-height: 100%;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
body {
|
|
40
|
+
background:
|
|
41
|
+
radial-gradient(circle at 0% 0%, rgba(13, 110, 189, 0.22), transparent 34%),
|
|
42
|
+
radial-gradient(circle at 88% 2%, rgba(14, 165, 233, 0.12), transparent 36%),
|
|
43
|
+
linear-gradient(180deg, #081221 0%, #0a1528 46%, #0b182c 100%);
|
|
44
|
+
color: var(--text);
|
|
45
|
+
font-family: "Source Han Serif SC", "Noto Serif SC", "Songti SC", "STSong", Georgia, serif;
|
|
46
|
+
font-size: 18px;
|
|
47
|
+
line-height: 1.82;
|
|
48
|
+
text-rendering: optimizeLegibility;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.page {
|
|
52
|
+
max-width: 1360px;
|
|
53
|
+
margin: 0 auto;
|
|
54
|
+
padding: 32px 24px 72px;
|
|
55
|
+
box-sizing: border-box;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.doc-panel {
|
|
59
|
+
min-width: 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.markdown-body {
|
|
63
|
+
background: linear-gradient(180deg, #12233d 0%, #0f1d33 100%);
|
|
64
|
+
border: 1px solid var(--border);
|
|
65
|
+
border-radius: 24px;
|
|
66
|
+
padding: 46px 56px 52px;
|
|
67
|
+
box-sizing: border-box;
|
|
68
|
+
color: var(--text);
|
|
69
|
+
box-shadow: 0 30px 62px -50px rgba(1, 6, 16, 0.9);
|
|
70
|
+
overflow-wrap: anywhere;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.markdown-body > :first-child {
|
|
74
|
+
margin-top: 0 !important;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.markdown-body > :last-child {
|
|
78
|
+
margin-bottom: 0 !important;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.markdown-body :is(h1, h2, h3, h4, h5, h6) {
|
|
82
|
+
font-family: "Source Han Sans SC", "Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif;
|
|
83
|
+
color: var(--heading);
|
|
84
|
+
letter-spacing: 0.01em;
|
|
85
|
+
line-height: 1.32;
|
|
86
|
+
margin-top: 1.7em;
|
|
87
|
+
margin-bottom: 0.62em;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.markdown-body h1 {
|
|
91
|
+
font-size: clamp(2rem, 1.35rem + 1.7vw, 2.5rem);
|
|
92
|
+
border-bottom: 1px solid var(--border);
|
|
93
|
+
padding-bottom: 0.34em;
|
|
94
|
+
margin-top: 0.2em;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.markdown-body h2 {
|
|
98
|
+
font-size: clamp(1.45rem, 1.2rem + 0.9vw, 1.9rem);
|
|
99
|
+
border-bottom: 1px solid color-mix(in srgb, var(--border) 86%, transparent);
|
|
100
|
+
padding-bottom: 0.24em;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.markdown-body h3 {
|
|
104
|
+
font-size: clamp(1.2rem, 1.1rem + 0.45vw, 1.46rem);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.markdown-body h4,
|
|
108
|
+
.markdown-body h5,
|
|
109
|
+
.markdown-body h6 {
|
|
110
|
+
font-size: 1.05rem;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.markdown-body p,
|
|
114
|
+
.markdown-body li,
|
|
115
|
+
.markdown-body td,
|
|
116
|
+
.markdown-body th,
|
|
117
|
+
.markdown-body dd,
|
|
118
|
+
.markdown-body dt {
|
|
119
|
+
color: var(--text);
|
|
120
|
+
font-size: 1.02rem;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.markdown-body p {
|
|
124
|
+
margin: 0.74em 0;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.markdown-body a {
|
|
128
|
+
color: var(--link);
|
|
129
|
+
text-decoration-thickness: 0.08em;
|
|
130
|
+
text-underline-offset: 0.18em;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.markdown-body a:hover {
|
|
134
|
+
color: var(--link-hover);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.markdown-body ul,
|
|
138
|
+
.markdown-body ol {
|
|
139
|
+
margin: 0.75em 0 1em;
|
|
140
|
+
padding-left: 1.5em;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.markdown-body li {
|
|
144
|
+
margin: 0.35em 0;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.markdown-body blockquote {
|
|
148
|
+
margin: 1.1em 0;
|
|
149
|
+
border-left: 4px solid var(--blockquote-border);
|
|
150
|
+
background: var(--blockquote-bg);
|
|
151
|
+
color: var(--text-muted);
|
|
152
|
+
padding: 0.85em 1em;
|
|
153
|
+
border-radius: 0 10px 10px 0;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.markdown-body code {
|
|
157
|
+
background: var(--code-bg);
|
|
158
|
+
color: var(--code-text);
|
|
159
|
+
border-radius: 6px;
|
|
160
|
+
padding: 0.14em 0.4em;
|
|
161
|
+
font-size: 0.9em;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.markdown-body pre {
|
|
165
|
+
margin: 1.05em 0;
|
|
166
|
+
border-radius: 14px;
|
|
167
|
+
padding: 16px 18px;
|
|
168
|
+
overflow: auto;
|
|
169
|
+
border: 1px solid #31496d;
|
|
170
|
+
background: #081324;
|
|
171
|
+
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.markdown-body pre code {
|
|
175
|
+
background: transparent;
|
|
176
|
+
color: #dbe7ff;
|
|
177
|
+
padding: 0;
|
|
178
|
+
border-radius: 0;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.markdown-body table {
|
|
182
|
+
width: 100%;
|
|
183
|
+
margin: 1.15em 0;
|
|
184
|
+
border-collapse: separate;
|
|
185
|
+
border-spacing: 0;
|
|
186
|
+
border: 1px solid var(--border);
|
|
187
|
+
border-radius: 12px;
|
|
188
|
+
overflow: hidden;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.markdown-body table th,
|
|
192
|
+
.markdown-body table td {
|
|
193
|
+
border: 0;
|
|
194
|
+
border-bottom: 1px solid var(--border);
|
|
195
|
+
border-right: 1px solid var(--border);
|
|
196
|
+
padding: 0.7em 0.78em;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.markdown-body table th:last-child,
|
|
200
|
+
.markdown-body table td:last-child {
|
|
201
|
+
border-right: 0;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.markdown-body table tr:last-child td {
|
|
205
|
+
border-bottom: 0;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.markdown-body table th {
|
|
209
|
+
background: var(--table-head);
|
|
210
|
+
color: var(--heading);
|
|
211
|
+
font-family: "Source Han Sans SC", "Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif;
|
|
212
|
+
font-size: 0.95rem;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.markdown-body table tr:nth-child(2n) td {
|
|
216
|
+
background: var(--table-stripe);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.markdown-body hr {
|
|
220
|
+
height: 1px;
|
|
221
|
+
border: 0;
|
|
222
|
+
background: color-mix(in srgb, var(--border) 82%, transparent);
|
|
223
|
+
margin: 1.8em 0;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.markdown-body img {
|
|
227
|
+
display: block;
|
|
228
|
+
max-width: 100%;
|
|
229
|
+
height: auto;
|
|
230
|
+
margin: 1.1em auto;
|
|
231
|
+
border-radius: 14px;
|
|
232
|
+
border: 1px solid color-mix(in srgb, var(--border) 92%, transparent);
|
|
233
|
+
box-shadow: 0 12px 24px -22px rgba(0, 0, 0, 0.85);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
@media (max-width: 1120px) {
|
|
237
|
+
.page {
|
|
238
|
+
padding: 74px 14px 38px;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.markdown-body {
|
|
242
|
+
padding: 34px 24px 38px;
|
|
243
|
+
border-radius: 18px;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
@media (max-width: 720px) {
|
|
248
|
+
body {
|
|
249
|
+
font-size: 16px;
|
|
250
|
+
line-height: 1.75;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.markdown-body {
|
|
254
|
+
padding: 28px 18px 30px;
|
|
255
|
+
}
|
|
256
|
+
}
|