支持目录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-07
- 编辑与预览区域样式优化2022-08-10 - 编辑与预览区域样式优化2022-08-10
- 左边栏文件资源管理支持搜索文件2022-08-17 - 左边栏文件资源管理支持搜索文件2022-08-17
- 支持[TOC]目录2022-09-04
## 国外开源版本弊端: ## 国外开源版本弊端:
- 作者已经不维护了 - 作者已经不维护了

View File

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

2
package-lock.json generated
View File

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

View File

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

View File

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

View File

@ -15,6 +15,7 @@ const zero = {
table: false, table: false,
tasklist: false, tasklist: false,
typographer: false, typographer: false,
toc: false,
}, },
// Emoji extension // Emoji extension
emoji: { emoji: {
@ -49,6 +50,13 @@ const zero = {
mermaid: { mermaid: {
enabled: false, enabled: false,
}, },
/*
Toc extension
[TOC] 转换为目录
*/
toc: {
enabled: false,
},
}; };
export default { export default {
@ -66,6 +74,7 @@ export default {
linkify: true, linkify: true,
table: true, table: true,
tasklist: true, tasklist: true,
toc: true,
}, },
emoji: { emoji: {
enabled: true, enabled: true,
@ -86,6 +95,7 @@ export default {
sup: true, sup: true,
table: true, table: true,
tasklist: true, tasklist: true,
toc: true,
typographer: true, typographer: true,
}, },
emoji: { 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) => { export default (md) => {
md.core.ruler.before('replacements', 'anchors', (state) => { md.core.ruler.before('replacements', 'anchors', (state) => {
const anchorHash = {}; const anchorHash = {};
let headingOpenToken; let headingOpenToken;
let headingContent; let headingContent;
const tocTokens = [];
const headings = [];
state.tokens.forEach((token) => { state.tokens.forEach((token) => {
if (token.type === 'heading_open') { if (token.type === 'heading_open') {
headingContent = ''; headingContent = '';
@ -39,6 +83,12 @@ export default (md) => {
headingOpenToken.attrs = [ headingOpenToken.attrs = [
['id', anchor], ['id', anchor],
]; ];
headings.push({
title: headingOpenToken.headingContent,
anchor: headingOpenToken.headingAnchor,
level: parseInt(headingOpenToken.tag.slice(1), 10),
children: [],
});
headingOpenToken = undefined; headingOpenToken = undefined;
} else if (headingOpenToken) { } else if (headingOpenToken) {
headingContent += token.children.reduce((result, child) => { headingContent += token.children.reduce((result, child) => {
@ -48,6 +98,33 @@ export default (md) => {
return result; 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) { if (tokens[idx].meta.subId > 0) {
id += `:${tokens[idx].meta.subId}`; 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]*$/, 'cl cl-hash': /-+[ \t]*$/,
}, },
}; };
grammars.main['cn-toc'] = {
pattern: /^\[TOC\]$/gm,
};
for (let i = 6; i >= 1; i -= 1) { for (let i = 6; i >= 1; i -= 1) {
grammars.main[`h${i} cn-head`] = { grammars.main[`h${i} cn-head`] = {
pattern: new RegExp(`^#{${i}}[ \t].+$`, 'gm'), pattern: new RegExp(`^#{${i}}[ \t].+$`, 'gm'),

View File

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

View File

@ -327,6 +327,17 @@
color: #95cc5e; 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 { .markdown-highlighting--inline {