zhangdocs 1.1.19 → 1.1.24
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/lib/build.js
CHANGED
|
@@ -32,7 +32,10 @@ var renderer = new marked.Renderer();
|
|
|
32
32
|
renderer.code = function (code, lang) {
|
|
33
33
|
if (lang === 'mermaid') {
|
|
34
34
|
// 为Mermaid图表生成特殊的div容器
|
|
35
|
-
|
|
35
|
+
// 转义 HTML 特殊字符,避免被解析为 HTML 标签
|
|
36
|
+
// 将 < 转为 <,> 转为 >
|
|
37
|
+
var escapedCode = code.replace(/</g, '<').replace(/>/g, '>');
|
|
38
|
+
return '<div class="mermaid">' + escapedCode + '</div>';
|
|
36
39
|
} else {
|
|
37
40
|
// 对于其他语言,使用默认的高亮处理
|
|
38
41
|
if (lang && highlight.getLanguage(lang)) {
|
|
@@ -186,6 +189,56 @@ function build(commander, changeFile) {
|
|
|
186
189
|
// 图片引用路径处理
|
|
187
190
|
markedstr = imgPath(markedstr, _path);
|
|
188
191
|
|
|
192
|
+
// 计算上一节和下一节的URL
|
|
193
|
+
var prevUrl = null;
|
|
194
|
+
var nextUrl = null;
|
|
195
|
+
var prevTitle = null;
|
|
196
|
+
var nextTitle = null;
|
|
197
|
+
|
|
198
|
+
if (idx > 0) {
|
|
199
|
+
// 上一节
|
|
200
|
+
var prevItem = pathArr[idx - 1];
|
|
201
|
+
var prevItemRel = path.normalize(prevItem);
|
|
202
|
+
prevItemRel = prevItemRel.replace(path.normalize(process.cwd() + '/md/'), '');
|
|
203
|
+
var prevPath = path.normalize(process.cwd() + '/html/' + prevItemRel).replace('.md', ".html");
|
|
204
|
+
// 计算从当前页面到上一节的相对路径
|
|
205
|
+
var relative = path.relative(path.dirname(_path), prevPath).replace(/\\/g, '/');
|
|
206
|
+
// 如果相对路径为空或为当前目录,使用文件名
|
|
207
|
+
if (!relative || relative === '.') {
|
|
208
|
+
prevUrl = path.basename(prevPath);
|
|
209
|
+
} else {
|
|
210
|
+
// 确保路径以 ./ 开头(如果不在同一目录)
|
|
211
|
+
if (!relative.startsWith('.')) {
|
|
212
|
+
prevUrl = './' + relative;
|
|
213
|
+
} else {
|
|
214
|
+
prevUrl = relative;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
prevTitle = path.basename(prevItem, '.md');
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (idx < pathArr.length - 1) {
|
|
221
|
+
// 下一节
|
|
222
|
+
var nextItem = pathArr[idx + 1];
|
|
223
|
+
var nextItemRel = path.normalize(nextItem);
|
|
224
|
+
nextItemRel = nextItemRel.replace(path.normalize(process.cwd() + '/md/'), '');
|
|
225
|
+
var nextPath = path.normalize(process.cwd() + '/html/' + nextItemRel).replace('.md', ".html");
|
|
226
|
+
// 计算从当前页面到下一节的相对路径
|
|
227
|
+
var relative = path.relative(path.dirname(_path), nextPath).replace(/\\/g, '/');
|
|
228
|
+
// 如果相对路径为空或为当前目录,使用文件名
|
|
229
|
+
if (!relative || relative === '.') {
|
|
230
|
+
nextUrl = path.basename(nextPath);
|
|
231
|
+
} else {
|
|
232
|
+
// 确保路径以 ./ 开头(如果不在同一目录)
|
|
233
|
+
if (!relative.startsWith('.')) {
|
|
234
|
+
nextUrl = './' + relative;
|
|
235
|
+
} else {
|
|
236
|
+
nextUrl = relative;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
nextTitle = path.basename(nextItem, '.md');
|
|
240
|
+
}
|
|
241
|
+
|
|
189
242
|
// ejs 模板生成HTML
|
|
190
243
|
html = file.ejs(template + '/layout.ejs', {
|
|
191
244
|
title: pkg.name,//项目工程名字
|
|
@@ -194,7 +247,11 @@ function build(commander, changeFile) {
|
|
|
194
247
|
relative_path: file.relativePath(_path, process.cwd()),//相对路径
|
|
195
248
|
menu_html: navHTML,//页面之间的超链接导航
|
|
196
249
|
toc_html: tocHTML,// markdown 导航
|
|
197
|
-
markdown_html: markedstr // markdown 生成字符串
|
|
250
|
+
markdown_html: markedstr, // markdown 生成字符串
|
|
251
|
+
prev_url: prevUrl, // 上一节URL
|
|
252
|
+
next_url: nextUrl, // 下一节URL
|
|
253
|
+
prev_title: prevTitle, // 上一节标题
|
|
254
|
+
next_title: nextTitle // 下一节标题
|
|
198
255
|
});
|
|
199
256
|
file.mkdirsSync(path.dirname(_path));
|
|
200
257
|
//写入指定目录中
|
package/package.json
CHANGED
package/theme/handbook/head.ejs
CHANGED
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
</title>
|
|
11
11
|
<link rel="stylesheet" href="<%=relative_path%>static/css/main.css">
|
|
12
12
|
<link rel="stylesheet" href="https://static.docs-hub.com/bootstrapmin_1645176572503.css">
|
|
13
|
+
<!-- Viewer.js 图片预览库 -->
|
|
14
|
+
<link rel="stylesheet" href="https://static.docs-hub.com/viewermin_1764834568346.css">
|
|
15
|
+
<script src="https://static.docs-hub.com/viewermin_1764834557521.js"></script>
|
|
13
16
|
<!-- MathJax 配置 -->
|
|
14
17
|
<script>
|
|
15
18
|
MathJax = {
|
|
@@ -8,6 +8,47 @@
|
|
|
8
8
|
<div class="content markdown-body <%= index ? 'index-page-content' : '' %>">
|
|
9
9
|
|
|
10
10
|
<%- markdown_html %>
|
|
11
|
+
|
|
12
|
+
<% if (!index) { %>
|
|
13
|
+
<!-- 上一节/下一节导航按钮 -->
|
|
14
|
+
<div class="page-navigation">
|
|
15
|
+
<% if (typeof prev_url !== 'undefined' && prev_url) { %>
|
|
16
|
+
<a href="<%= prev_url %>" class="nav-button nav-prev" id="nav-prev">
|
|
17
|
+
<span class="nav-arrow">←</span>
|
|
18
|
+
<span class="nav-text">
|
|
19
|
+
<span class="nav-label">上一节</span>
|
|
20
|
+
<span class="nav-title"><%= prev_title || '上一节' %></span>
|
|
21
|
+
</span>
|
|
22
|
+
</a>
|
|
23
|
+
<% } else { %>
|
|
24
|
+
<div class="nav-button nav-prev nav-disabled">
|
|
25
|
+
<span class="nav-arrow">←</span>
|
|
26
|
+
<span class="nav-text">
|
|
27
|
+
<span class="nav-label">上一节</span>
|
|
28
|
+
<span class="nav-title">没有上一节</span>
|
|
29
|
+
</span>
|
|
30
|
+
</div>
|
|
31
|
+
<% } %>
|
|
32
|
+
|
|
33
|
+
<% if (typeof next_url !== 'undefined' && next_url) { %>
|
|
34
|
+
<a href="<%= next_url %>" class="nav-button nav-next" id="nav-next">
|
|
35
|
+
<span class="nav-text">
|
|
36
|
+
<span class="nav-label">下一节</span>
|
|
37
|
+
<span class="nav-title"><%= next_title || '下一节' %></span>
|
|
38
|
+
</span>
|
|
39
|
+
<span class="nav-arrow">→</span>
|
|
40
|
+
</a>
|
|
41
|
+
<% } else { %>
|
|
42
|
+
<div class="nav-button nav-next nav-disabled">
|
|
43
|
+
<span class="nav-text">
|
|
44
|
+
<span class="nav-label">下一节</span>
|
|
45
|
+
<span class="nav-title">没有下一节</span>
|
|
46
|
+
</span>
|
|
47
|
+
<span class="nav-arrow">→</span>
|
|
48
|
+
</div>
|
|
49
|
+
<% } %>
|
|
50
|
+
</div>
|
|
51
|
+
<% } %>
|
|
11
52
|
</div>
|
|
12
53
|
</div>
|
|
13
54
|
|
|
@@ -292,5 +333,110 @@ style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background:
|
|
|
292
333
|
// 延迟初始化(处理动态显示的情况)
|
|
293
334
|
setTimeout(initMenuPopup, 500);
|
|
294
335
|
setTimeout(initMenuPopup, 1000);
|
|
336
|
+
|
|
337
|
+
// 图片预览功能初始化
|
|
338
|
+
(function() {
|
|
339
|
+
var imageViewer = null;
|
|
340
|
+
|
|
341
|
+
function initImageViewer() {
|
|
342
|
+
// 查找markdown-body容器
|
|
343
|
+
var markdownBody = document.querySelector('.markdown-body');
|
|
344
|
+
if (!markdownBody) {
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// 查找所有图片
|
|
349
|
+
var images = markdownBody.querySelectorAll('img');
|
|
350
|
+
if (images.length === 0) {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// 如果Viewer已经初始化,先销毁
|
|
355
|
+
if (imageViewer) {
|
|
356
|
+
try {
|
|
357
|
+
imageViewer.destroy();
|
|
358
|
+
} catch(e) {
|
|
359
|
+
// 忽略错误
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// 为图片添加样式和点击事件
|
|
364
|
+
images.forEach(function(img) {
|
|
365
|
+
// 跳过已经是链接内的图片
|
|
366
|
+
if (img.parentElement.tagName === 'A') {
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// 添加样式
|
|
371
|
+
img.style.cursor = 'zoom-in';
|
|
372
|
+
img.classList.add('image-preview');
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
// 初始化Viewer.js - 直接对markdown-body容器初始化
|
|
376
|
+
try {
|
|
377
|
+
imageViewer = new Viewer(markdownBody, {
|
|
378
|
+
inline: false,
|
|
379
|
+
viewed: function() {
|
|
380
|
+
// 查看图片时的回调
|
|
381
|
+
},
|
|
382
|
+
hidden: function() {
|
|
383
|
+
// 关闭查看器时的回调
|
|
384
|
+
},
|
|
385
|
+
toolbar: {
|
|
386
|
+
zoomIn: true,
|
|
387
|
+
zoomOut: true,
|
|
388
|
+
oneToOne: true,
|
|
389
|
+
reset: true,
|
|
390
|
+
prev: true,
|
|
391
|
+
play: false,
|
|
392
|
+
next: true,
|
|
393
|
+
rotateLeft: true,
|
|
394
|
+
rotateRight: true,
|
|
395
|
+
flipHorizontal: true,
|
|
396
|
+
flipVertical: true
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
} catch(e) {
|
|
400
|
+
console.error('图片预览初始化失败:', e);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// 立即尝试初始化
|
|
405
|
+
initImageViewer();
|
|
406
|
+
|
|
407
|
+
// DOM加载完成后初始化
|
|
408
|
+
if (document.readyState === 'loading') {
|
|
409
|
+
document.addEventListener('DOMContentLoaded', initImageViewer);
|
|
410
|
+
} else {
|
|
411
|
+
setTimeout(initImageViewer, 100);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// 延迟初始化(处理动态显示的情况)
|
|
415
|
+
setTimeout(initImageViewer, 500);
|
|
416
|
+
setTimeout(initImageViewer, 1000);
|
|
417
|
+
})();
|
|
418
|
+
|
|
419
|
+
// 上一节/下一节导航功能
|
|
420
|
+
(function() {
|
|
421
|
+
var prevButton = document.getElementById('nav-prev');
|
|
422
|
+
var nextButton = document.getElementById('nav-next');
|
|
423
|
+
|
|
424
|
+
// 键盘快捷键:Ctrl + 左箭头(上一节),Ctrl + 右箭头(下一节)
|
|
425
|
+
document.addEventListener('keydown', function(e) {
|
|
426
|
+
// 检查是否按下了Ctrl键
|
|
427
|
+
if (e.ctrlKey || e.metaKey) {
|
|
428
|
+
// Ctrl + 左箭头:上一节
|
|
429
|
+
if (e.key === 'ArrowLeft' && prevButton && prevButton.href) {
|
|
430
|
+
e.preventDefault();
|
|
431
|
+
window.location.href = prevButton.href;
|
|
432
|
+
}
|
|
433
|
+
// Ctrl + 右箭头:下一节
|
|
434
|
+
else if (e.key === 'ArrowRight' && nextButton && nextButton.href) {
|
|
435
|
+
e.preventDefault();
|
|
436
|
+
window.location.href = nextButton.href;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
})();
|
|
295
441
|
</script>
|
|
296
442
|
<% include footer.ejs %>
|
|
@@ -22,9 +22,14 @@
|
|
|
22
22
|
max-width: 100%
|
|
23
23
|
height: auto
|
|
24
24
|
border-radius: 4px
|
|
25
|
+
transition: opacity 0.2s ease
|
|
25
26
|
@media mq-mobile
|
|
26
27
|
display: block
|
|
27
28
|
margin: 12px auto
|
|
29
|
+
&.image-preview
|
|
30
|
+
cursor: zoom-in
|
|
31
|
+
&:hover
|
|
32
|
+
opacity: 0.9
|
|
28
33
|
a
|
|
29
34
|
color: #0969da
|
|
30
35
|
text-decoration: none
|
|
@@ -440,4 +440,76 @@ table
|
|
|
440
440
|
background: $secondary-color
|
|
441
441
|
color: $primary-color
|
|
442
442
|
border-left-color: $primary-color
|
|
443
|
-
transform: translateX(5px)
|
|
443
|
+
transform: translateX(5px)
|
|
444
|
+
|
|
445
|
+
// 上一节/下一节导航按钮
|
|
446
|
+
.page-navigation
|
|
447
|
+
display: flex
|
|
448
|
+
justify-content: space-between
|
|
449
|
+
align-items: center
|
|
450
|
+
margin-top: 40px
|
|
451
|
+
padding-top: 30px
|
|
452
|
+
border-top: 1px solid $border-color
|
|
453
|
+
gap: 20px
|
|
454
|
+
|
|
455
|
+
.nav-button
|
|
456
|
+
display: flex
|
|
457
|
+
align-items: center
|
|
458
|
+
padding: 12px 20px
|
|
459
|
+
background: #fff
|
|
460
|
+
border: 1px solid $border-color
|
|
461
|
+
border-radius: 6px
|
|
462
|
+
text-decoration: none
|
|
463
|
+
color: $text-color
|
|
464
|
+
transition: all 0.2s ease
|
|
465
|
+
flex: 1
|
|
466
|
+
max-width: 45%
|
|
467
|
+
|
|
468
|
+
&:hover
|
|
469
|
+
background: $secondary-color
|
|
470
|
+
border-color: $primary-color
|
|
471
|
+
color: $primary-color
|
|
472
|
+
transform: translateY(-2px)
|
|
473
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1)
|
|
474
|
+
|
|
475
|
+
&.nav-disabled
|
|
476
|
+
opacity: 0.5
|
|
477
|
+
cursor: not-allowed
|
|
478
|
+
pointer-events: none
|
|
479
|
+
|
|
480
|
+
.nav-arrow
|
|
481
|
+
font-size: 20px
|
|
482
|
+
font-weight: bold
|
|
483
|
+
margin: 0 10px
|
|
484
|
+
|
|
485
|
+
.nav-text
|
|
486
|
+
display: flex
|
|
487
|
+
flex-direction: column
|
|
488
|
+
flex: 1
|
|
489
|
+
|
|
490
|
+
.nav-label
|
|
491
|
+
font-size: 12px
|
|
492
|
+
color: #999
|
|
493
|
+
margin-bottom: 4px
|
|
494
|
+
text-transform: uppercase
|
|
495
|
+
letter-spacing: 0.5px
|
|
496
|
+
|
|
497
|
+
.nav-title
|
|
498
|
+
font-size: 14px
|
|
499
|
+
font-weight: 500
|
|
500
|
+
line-height: 1.4
|
|
501
|
+
|
|
502
|
+
&.nav-prev
|
|
503
|
+
text-align: left
|
|
504
|
+
|
|
505
|
+
&.nav-next
|
|
506
|
+
text-align: right
|
|
507
|
+
flex-direction: row-reverse
|
|
508
|
+
|
|
509
|
+
@media mq-mobile
|
|
510
|
+
flex-direction: column
|
|
511
|
+
gap: 15px
|
|
512
|
+
|
|
513
|
+
.nav-button
|
|
514
|
+
max-width: 100%
|
|
515
|
+
width: 100%
|