zhangdocs 1.1.20 → 1.1.25

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
- return '<div class="mermaid">' + code + '</div>';
35
+ // 转义 HTML 特殊字符,避免被解析为 HTML 标签
36
+ // 将 < 转为 &lt;,> 转为 &gt;
37
+ var escapedCode = code.replace(/</g, '&lt;').replace(/>/g, '&gt;');
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zhangdocs",
3
- "version": "1.1.20",
3
+ "version": "1.1.25",
4
4
  "description": "Simple document generation tool. Dependence Node.js run.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -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
 
@@ -18,14 +59,16 @@ style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background:
18
59
  style="background: white; padding: 30px; border-radius: 10px; box-shadow: 0 4px 20px rgba(0,0,0,0.3); max-width: 400px; width: 90%;">
19
60
  <h2 style="margin: 0 0 20px 0; text-align: center; color: #333;">访问验证</h2>
20
61
  <p style="margin: 0 0 15px 0; color: #666; text-align: center;">请输入访问令牌</p>
21
- <input type="password" id="token-input" placeholder="请输入token"
22
- style="width: 100%; padding: 12px; border: 1px solid #ddd; border-radius: 5px; font-size: 16px; margin-bottom: 15px; box-sizing: border-box;">
23
- <div style="text-align: center;">
24
- <button id="token-submit"
25
- style="background: #007bff; color: white; border: none; padding: 12px 30px; border-radius: 5px; font-size: 16px; cursor: pointer; margin-right: 10px;">验证</button>
26
- <button id="token-cancel"
27
- style="background: #6c757d; color: white; border: none; padding: 12px 30px; border-radius: 5px; font-size: 16px; cursor: pointer;">取消</button>
28
- </div>
62
+ <form id="token-form" onsubmit="return false;">
63
+ <input type="password" id="token-input" placeholder="请输入token"
64
+ style="width: 100%; padding: 12px; border: 1px solid #ddd; border-radius: 5px; font-size: 16px; margin-bottom: 15px; box-sizing: border-box;">
65
+ <div style="text-align: center;">
66
+ <button type="submit" id="token-submit"
67
+ style="background: #007bff; color: white; border: none; padding: 12px 30px; border-radius: 5px; font-size: 16px; cursor: pointer; margin-right: 10px;">验证</button>
68
+ <button type="button" id="token-cancel"
69
+ style="background: #6c757d; color: white; border: none; padding: 12px 30px; border-radius: 5px; font-size: 16px; cursor: pointer;">取消</button>
70
+ </div>
71
+ </form>
29
72
  <div id="error-message" style="color: red; text-align: center; margin-top: 10px; display: none;">Token不正确,请重新输入
30
73
  </div>
31
74
  </div>
@@ -151,7 +194,10 @@ style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background:
151
194
  tokenModal.style.display = 'flex';
152
195
 
153
196
  // 验证token
154
- function verifyToken() {
197
+ function verifyToken(e) {
198
+ if (e) {
199
+ e.preventDefault(); // 防止表单提交刷新页面
200
+ }
155
201
  const inputToken = tokenInput.value.trim();
156
202
  if (inputToken === expectedToken) {
157
203
  localStorage.setItem('token', inputToken);
@@ -162,6 +208,7 @@ style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background:
162
208
  tokenInput.value = '';
163
209
  tokenInput.focus();
164
210
  }
211
+ return false; // 防止表单提交
165
212
  }
166
213
 
167
214
  // 显示内容
@@ -181,6 +228,10 @@ style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background:
181
228
  }
182
229
 
183
230
  // 事件监听
231
+ const tokenForm = document.getElementById('token-form');
232
+ if (tokenForm) {
233
+ tokenForm.addEventListener('submit', verifyToken);
234
+ }
184
235
  tokenSubmit.addEventListener('click', verifyToken);
185
236
  tokenCancel.addEventListener('click', function () {
186
237
  hideContent();
@@ -189,7 +240,8 @@ style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background:
189
240
 
190
241
  tokenInput.addEventListener('keypress', function (e) {
191
242
  if (e.key === 'Enter') {
192
- verifyToken();
243
+ e.preventDefault();
244
+ verifyToken(e);
193
245
  }
194
246
  });
195
247
 
@@ -374,5 +426,28 @@ style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background:
374
426
  setTimeout(initImageViewer, 500);
375
427
  setTimeout(initImageViewer, 1000);
376
428
  })();
429
+
430
+ // 上一节/下一节导航功能
431
+ (function() {
432
+ var prevButton = document.getElementById('nav-prev');
433
+ var nextButton = document.getElementById('nav-next');
434
+
435
+ // 键盘快捷键:Ctrl + 左箭头(上一节),Ctrl + 右箭头(下一节)
436
+ document.addEventListener('keydown', function(e) {
437
+ // 检查是否按下了Ctrl键
438
+ if (e.ctrlKey || e.metaKey) {
439
+ // Ctrl + 左箭头:上一节
440
+ if (e.key === 'ArrowLeft' && prevButton && prevButton.href) {
441
+ e.preventDefault();
442
+ window.location.href = prevButton.href;
443
+ }
444
+ // Ctrl + 右箭头:下一节
445
+ else if (e.key === 'ArrowRight' && nextButton && nextButton.href) {
446
+ e.preventDefault();
447
+ window.location.href = nextButton.href;
448
+ }
449
+ }
450
+ });
451
+ })();
377
452
  </script>
378
453
  <% include footer.ejs %>
@@ -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%