diff --git a/README.md b/README.md
index afde4311..25cb6976 100644
--- a/README.md
+++ b/README.md
@@ -34,6 +34,7 @@ StackEdit中文版的docker镜像地址:[mafgwo/stackedit](https://hub.docker.
- 支持了右上角一键切换主题,补全了深色主题的样式(2022-08-07)
- 编辑与预览区域样式优化(2022-08-10)
- 左边栏文件资源管理支持搜索文件(2022-08-17)
+- 支持[TOC]目录(2022-09-04)
## 国外开源版本弊端:
- 作者已经不维护了
diff --git a/chrome-app/manifest.json b/chrome-app/manifest.json
index b4c708c7..41725db8 100644
--- a/chrome-app/manifest.json
+++ b/chrome-app/manifest.json
@@ -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",
diff --git a/package-lock.json b/package-lock.json
index 3b8384a8..27227349 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "stackedit",
- "version": "5.15.11",
+ "version": "5.15.12",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/package.json b/package.json
index 957eb761..bb269509 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "stackedit",
- "version": "5.15.11",
+ "version": "5.15.12",
"description": "免费, 开源, 功能齐全的 Markdown 编辑器",
"author": "Benoit Schweblin, 豆萁",
"license": "Apache-2.0",
diff --git a/src/components/CodeEditor.vue b/src/components/CodeEditor.vue
index 1ea9596b..ab36ad5d 100644
--- a/src/components/CodeEditor.vue
+++ b/src/components/CodeEditor.vue
@@ -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;
diff --git a/src/data/presets.js b/src/data/presets.js
index b37095c5..fb60a502 100644
--- a/src/data/presets.js
+++ b/src/data/presets.js
@@ -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: {
diff --git a/src/extensions/libs/markdownItAnchor.js b/src/extensions/libs/markdownItAnchor.js
index 70061878..8abeab1c 100644
--- a/src/extensions/libs/markdownItAnchor.js
+++ b/src/extensions/libs/markdownItAnchor.js
@@ -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 = `
`;
+ if (item.anchor && item.title) {
+ result += `${item.title}`;
+ }
+ result += arrayToHtml(item.children);
+ return `${result}`;
+ }).join('\n');
+ return ``;
+}
+
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 = `${tocHtml}
`;
+ tocToken.children.unshift(tocDiv);
+ tocToken.children[1].content = '';
+ tocToken.content = '';
+ });
+ }
});
};
diff --git a/src/extensions/markdownExtension.js b/src/extensions/markdownExtension.js
index 405a3825..f2c33e05 100644
--- a/src/extensions/markdownExtension.js
+++ b/src/extensions/markdownExtension.js
@@ -134,7 +134,7 @@ extensionSvc.onInitConverter(0, (markdown, options) => {
if (tokens[idx].meta.subId > 0) {
id += `:${tokens[idx].meta.subId}`;
}
- return ``;
+ return ``;
};
});
diff --git a/src/services/markdownGrammarSvc.js b/src/services/markdownGrammarSvc.js
index 217e7cd4..b7601ec0 100644
--- a/src/services/markdownGrammarSvc.js
+++ b/src/services/markdownGrammarSvc.js
@@ -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'),
diff --git a/src/styles/base.scss b/src/styles/base.scss
index 7e128d4a..1bf3dd18 100644
--- a/src/styles/base.scss
+++ b/src/styles/base.scss
@@ -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;
}
diff --git a/src/styles/markdownHighlighting.scss b/src/styles/markdownHighlighting.scss
index 8acf9bd0..65bcebf7 100644
--- a/src/styles/markdownHighlighting.scss
+++ b/src/styles/markdownHighlighting.scss
@@ -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 {