支持目录TOC

This commit is contained in:
xiaoqi.cxq 2022-09-04 17:28:24 +08:00
parent a957432749
commit fc74346b4b
11 changed files with 116 additions and 5 deletions

View File

@ -34,6 +34,7 @@ StackEdit中文版的docker镜像地址[mafgwo/stackedit](https://hub.docker.
- 支持了右上角一键切换主题补全了深色主题的样式2022-08-07
- 编辑与预览区域样式优化2022-08-10
- 左边栏文件资源管理支持搜索文件2022-08-17
- 支持[TOC]目录2022-09-04
## 国外开源版本弊端:
- 作者已经不维护了

View File

@ -1,7 +1,7 @@
{
"name": "StackEdit中文版",
"description": "支持Gitee仓库/粘贴图片自动上传的浏览器内 Markdown 编辑器",
"version": "5.15.11",
"version": "5.15.12",
"manifest_version": 2,
"container" : "GITEE",
"api_console_project_id" : "241271498917",

2
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "stackedit",
"version": "5.15.11",
"version": "5.15.12",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "stackedit",
"version": "5.15.11",
"version": "5.15.12",
"description": "免费, 开源, 功能齐全的 Markdown 编辑器",
"author": "Benoit Schweblin, 豆萁",
"license": "Apache-2.0",

View File

@ -43,6 +43,10 @@ export default {
overflow: auto;
padding: 0.2em 0.4em;
.app--dark & {
caret-color: $editor-color-dark-low;
}
* {
line-height: $line-height-base;
font-size: inherit !important;

View File

@ -15,6 +15,7 @@ const zero = {
table: false,
tasklist: false,
typographer: false,
toc: false,
},
// Emoji extension
emoji: {
@ -49,6 +50,13 @@ const zero = {
mermaid: {
enabled: false,
},
/*
Toc extension
[TOC] 转换为目录
*/
toc: {
enabled: false,
},
};
export default {
@ -66,6 +74,7 @@ export default {
linkify: true,
table: true,
tasklist: true,
toc: true,
},
emoji: {
enabled: true,
@ -86,6 +95,7 @@ export default {
sup: true,
table: true,
tasklist: true,
toc: true,
typographer: true,
},
emoji: {

View File

@ -1,8 +1,52 @@
function groupHeadings(headings, level = 1) {
const result = [];
let currentItem;
function pushCurrentItem() {
if (currentItem) {
if (currentItem.children.length > 0) {
currentItem.children = groupHeadings(currentItem.children, level + 1);
}
result.push(currentItem);
}
}
headings.forEach((heading) => {
if (heading.level !== level) {
currentItem = currentItem || {
children: [],
};
currentItem.children.push(heading);
} else {
pushCurrentItem();
currentItem = heading;
}
});
pushCurrentItem();
return result;
}
function arrayToHtml(arr) {
if (!arr || !arr.length) {
return '';
}
const ulHtml = arr.map((item) => {
let result = `<li class="preview-toc-item preview-toc-l${item.level}">`;
if (item.anchor && item.title) {
result += `<a href="#${item.anchor}">${item.title}</a>`;
}
result += arrayToHtml(item.children);
return `${result}</li>`;
}).join('\n');
return `<ul>${ulHtml}</ul>`;
}
export default (md) => {
md.core.ruler.before('replacements', 'anchors', (state) => {
const anchorHash = {};
let headingOpenToken;
let headingContent;
const tocTokens = [];
const headings = [];
state.tokens.forEach((token) => {
if (token.type === 'heading_open') {
headingContent = '';
@ -39,6 +83,12 @@ export default (md) => {
headingOpenToken.attrs = [
['id', anchor],
];
headings.push({
title: headingOpenToken.headingContent,
anchor: headingOpenToken.headingAnchor,
level: parseInt(headingOpenToken.tag.slice(1), 10),
children: [],
});
headingOpenToken = undefined;
} else if (headingOpenToken) {
headingContent += token.children.reduce((result, child) => {
@ -48,6 +98,33 @@ export default (md) => {
return result;
}, '');
}
if (token.type === 'inline' && token.content === '[TOC]') {
tocTokens.push(token);
}
});
// 没有TOC 直接返回
if (tocTokens.length === 0) {
return;
}
// 没有header
if (headings.length === 0) {
// 置空[TOC]文案为空字符串
tocTokens.forEach((tocToken) => {
tocToken.children[0].content = '';
tocToken.content = '';
});
} else {
tocTokens.forEach((tocToken) => {
// toc目录
const toc = groupHeadings(headings);
// 拼接为html
const tocHtml = arrayToHtml(toc);
const tocDiv = new state.Token('html_inline', '', 0);
tocDiv.content = `<div class="preview-toc">${tocHtml}</div>`;
tocToken.children.unshift(tocDiv);
tocToken.children[1].content = '';
tocToken.content = '';
});
}
});
};

View File

@ -134,7 +134,7 @@ extensionSvc.onInitConverter(0, (markdown, options) => {
if (tokens[idx].meta.subId > 0) {
id += `:${tokens[idx].meta.subId}`;
}
return `<sup class="footnote-ref"><a href="#fn${n}" id="${id}">${n}</a></sup>`;
return `<sup class="footnote-ref"><a href="#fn${n}" id="${id}">[${n}]</a></sup>`;
};
});

View File

@ -106,6 +106,9 @@ export default {
'cl cl-hash': /-+[ \t]*$/,
},
};
grammars.main['cn-toc'] = {
pattern: /^\[TOC\]$/gm,
};
for (let i = 6; i >= 1; i -= 1) {
grammars.main[`h${i} cn-head`] = {
pattern: new RegExp(`^#{${i}}[ \t].+$`, 'gm'),

View File

@ -23,6 +23,11 @@ body {
color: $body-color-dark;
}
.preview-toc ul {
list-style-type: none;
margin-bottom: 15px;
}
p,
blockquote,
pre,
@ -43,7 +48,7 @@ h3,
h4,
h5,
h6 {
margin: 1.8em 0 0.9em;
margin: 1.2em 0 0.9em;
line-height: $line-height-title;
}

View File

@ -327,6 +327,17 @@
color: #95cc5e;
}
}
.cn-toc {
color: #d7a55b;
font-size: 2.5em;
padding: 0.2em;
background-color: rgba(0, 0, 0, 0.1);
.app--dark & {
background-color: rgba(0, 0, 0, 0.3);
}
}
}
.markdown-highlighting--inline {