cherry-muse 1.0.1
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/LICENSE +162 -0
- package/README.md +139 -0
- package/dist/addons/cherry-code-block-card-plugin.js +1 -0
- package/dist/addons/cherry-code-block-echarts-plugin.js +1 -0
- package/dist/addons/cherry-code-block-mermaid-plugin.js +1 -0
- package/dist/cherry-markdown.core.common.js +1 -0
- package/dist/cherry-markdown.core.js +1 -0
- package/dist/cherry-markdown.css +4089 -0
- package/dist/cherry-markdown.engine.core.common.js +1 -0
- package/dist/cherry-markdown.engine.core.esm.js +1 -0
- package/dist/cherry-markdown.engine.core.js +1 -0
- package/dist/cherry-markdown.esm.js +1 -0
- package/dist/cherry-markdown.js +70837 -0
- package/dist/cherry-markdown.js.map +1 -0
- package/dist/cherry-markdown.min.css +1 -0
- package/dist/cherry-markdown.min.js +1 -0
- package/dist/stats.html +4838 -0
- package/examples/scripts/pinyin/README.md +53 -0
- package/package.json +167 -0
- package/src/Cherry.config.js +411 -0
- package/src/Cherry.js +788 -0
- package/src/CherryStatic.js +70 -0
- package/src/Editor.js +746 -0
- package/src/Engine.js +334 -0
- package/src/Event.js +74 -0
- package/src/Factory.js +180 -0
- package/src/Logger.js +31 -0
- package/src/Previewer.js +1147 -0
- package/src/Sanitizer.js +4 -0
- package/src/Sanitizer.node.js +7 -0
- package/src/Stats.js +101 -0
- package/src/Theme.js +46 -0
- package/src/UrlCache.js +98 -0
- package/src/addons/cherry-code-block-card-plugin.js +213 -0
- package/src/addons/cherry-code-block-echarts-plugin.js +161 -0
- package/src/addons/cherry-code-block-mermaid-plugin.js +118 -0
- package/src/core/HookCenter.js +303 -0
- package/src/core/HooksConfig.js +106 -0
- package/src/core/ParagraphBase.js +314 -0
- package/src/core/SentenceBase.js +65 -0
- package/src/core/SyntaxBase.js +197 -0
- package/src/core/hooks/AutoLink.js +251 -0
- package/src/core/hooks/BackgroundColor.js +46 -0
- package/src/core/hooks/Badge.js +100 -0
- package/src/core/hooks/Blockquote.js +113 -0
- package/src/core/hooks/Br.js +85 -0
- package/src/core/hooks/CodeBlock.js +876 -0
- package/src/core/hooks/Color.js +78 -0
- package/src/core/hooks/CommentReference.js +96 -0
- package/src/core/hooks/Detail.js +138 -0
- package/src/core/hooks/Emoji.config.js +9388 -0
- package/src/core/hooks/Emoji.js +223 -0
- package/src/core/hooks/Emphasis.js +113 -0
- package/src/core/hooks/Footnote.js +125 -0
- package/src/core/hooks/FrontMatter.js +52 -0
- package/src/core/hooks/FrontMatterVars.js +82 -0
- package/src/core/hooks/Header.js +229 -0
- package/src/core/hooks/HighLight.js +37 -0
- package/src/core/hooks/Hr.js +52 -0
- package/src/core/hooks/HtmlBlock.js +159 -0
- package/src/core/hooks/Iframe.js +80 -0
- package/src/core/hooks/Image.js +276 -0
- package/src/core/hooks/InlineCode.js +45 -0
- package/src/core/hooks/InlineMath.js +142 -0
- package/src/core/hooks/Link.js +169 -0
- package/src/core/hooks/List.js +260 -0
- package/src/core/hooks/Mark.js +55 -0
- package/src/core/hooks/MathBlock.js +97 -0
- package/src/core/hooks/Panel.js +170 -0
- package/src/core/hooks/Paragraph.js +84 -0
- package/src/core/hooks/Ruby.js +34 -0
- package/src/core/hooks/Size.js +84 -0
- package/src/core/hooks/Strikethrough.js +54 -0
- package/src/core/hooks/Sub.js +47 -0
- package/src/core/hooks/SuggestList.js +317 -0
- package/src/core/hooks/Suggester.js +759 -0
- package/src/core/hooks/Sup.js +47 -0
- package/src/core/hooks/Table.js +315 -0
- package/src/core/hooks/Toc.js +290 -0
- package/src/core/hooks/Transfer.js +47 -0
- package/src/core/hooks/Underline.js +37 -0
- package/src/index.core.js +29 -0
- package/src/index.engine.core.js +62 -0
- package/src/index.engine.js +30 -0
- package/src/index.js +28 -0
- package/src/locales/index.js +21 -0
- package/src/locales/zh_CN.js +170 -0
- package/src/sass/cherry.scss +122 -0
- package/src/sass/components/bubble.scss +122 -0
- package/src/sass/components/codemirror.scss +628 -0
- package/src/sass/components/dropdown.scss +37 -0
- package/src/sass/components/editor.scss +78 -0
- package/src/sass/components/preview.scss +71 -0
- package/src/sass/components/prism.scss +142 -0
- package/src/sass/components/stats.scss +32 -0
- package/src/sass/components/toc.scss +184 -0
- package/src/sass/components/toolbar.scss +117 -0
- package/src/sass/core/AutoLink.scss +20 -0
- package/src/sass/core/BackgroundColor.scss +0 -0
- package/src/sass/core/Badge.scss +116 -0
- package/src/sass/core/Blockquote.scss +12 -0
- package/src/sass/core/Br.scss +0 -0
- package/src/sass/core/Card.scss +219 -0
- package/src/sass/core/CodeBlock.scss +205 -0
- package/src/sass/core/Color.scss +37 -0
- package/src/sass/core/CommentReference.scss +0 -0
- package/src/sass/core/Detail.scss +107 -0
- package/src/sass/core/Emoji.scss +127 -0
- package/src/sass/core/Emphasis.scss +9 -0
- package/src/sass/core/Footnote.scss +21 -0
- package/src/sass/core/FrontMatterVars.scss +19 -0
- package/src/sass/core/Header.scss +103 -0
- package/src/sass/core/HighLight.scss +0 -0
- package/src/sass/core/Hr.scss +10 -0
- package/src/sass/core/HtmlBlock.scss +0 -0
- package/src/sass/core/Iframe.scss +36 -0
- package/src/sass/core/Image.scss +59 -0
- package/src/sass/core/InlineCode.scss +10 -0
- package/src/sass/core/InlineMath.scss +11 -0
- package/src/sass/core/Link.scss +16 -0
- package/src/sass/core/List.scss +61 -0
- package/src/sass/core/Mark.scss +15 -0
- package/src/sass/core/MathBlock.scss +0 -0
- package/src/sass/core/Panel.scss +150 -0
- package/src/sass/core/Paragraph.scss +6 -0
- package/src/sass/core/Ruby.scss +0 -0
- package/src/sass/core/Size.scss +8 -0
- package/src/sass/core/Strikethrough.scss +0 -0
- package/src/sass/core/Sub.scss +5 -0
- package/src/sass/core/Suggester.scss +62 -0
- package/src/sass/core/Sup.scss +5 -0
- package/src/sass/core/Table.scss +127 -0
- package/src/sass/core/Toc.scss +28 -0
- package/src/sass/core/Transfer.scss +0 -0
- package/src/sass/core/Underline.scss +0 -0
- package/src/sass/google-fonts.scss +34 -0
- package/src/sass/index.scss +3 -0
- package/src/sass/prism/dark.scss +131 -0
- package/src/sass/prism/light.scss +143 -0
- package/src/sass/variables/colors.scss +96 -0
- package/src/toolbars/Bubble.js +232 -0
- package/src/toolbars/BubbleTable.js +147 -0
- package/src/toolbars/HookCenter.js +185 -0
- package/src/toolbars/MenuBase.js +357 -0
- package/src/toolbars/PreviewerBubble.js +558 -0
- package/src/toolbars/Toc.js +246 -0
- package/src/toolbars/Toolbar.js +401 -0
- package/src/toolbars/hooks/Audio.js +53 -0
- package/src/toolbars/hooks/Badge.js +80 -0
- package/src/toolbars/hooks/BarTable.js +41 -0
- package/src/toolbars/hooks/Bold.js +70 -0
- package/src/toolbars/hooks/Br.js +34 -0
- package/src/toolbars/hooks/Card.js +64 -0
- package/src/toolbars/hooks/CheckList.js +41 -0
- package/src/toolbars/hooks/Code.js +46 -0
- package/src/toolbars/hooks/Color.js +285 -0
- package/src/toolbars/hooks/Copy.js +139 -0
- package/src/toolbars/hooks/Detail.js +70 -0
- package/src/toolbars/hooks/ECharts.js +303 -0
- package/src/toolbars/hooks/Emoji.js +303 -0
- package/src/toolbars/hooks/Export.js +47 -0
- package/src/toolbars/hooks/File.js +54 -0
- package/src/toolbars/hooks/Formula.js +36 -0
- package/src/toolbars/hooks/FullScreen.js +55 -0
- package/src/toolbars/hooks/Graph.js +281 -0
- package/src/toolbars/hooks/H1.js +71 -0
- package/src/toolbars/hooks/H2.js +71 -0
- package/src/toolbars/hooks/H3.js +71 -0
- package/src/toolbars/hooks/Header.js +100 -0
- package/src/toolbars/hooks/Hr.js +35 -0
- package/src/toolbars/hooks/Iframe.js +35 -0
- package/src/toolbars/hooks/Image.js +60 -0
- package/src/toolbars/hooks/Insert.js +36 -0
- package/src/toolbars/hooks/Italic.js +70 -0
- package/src/toolbars/hooks/LineTable.js +41 -0
- package/src/toolbars/hooks/Link.js +46 -0
- package/src/toolbars/hooks/List.js +55 -0
- package/src/toolbars/hooks/Ol.js +41 -0
- package/src/toolbars/hooks/Panel.js +155 -0
- package/src/toolbars/hooks/Quote.js +45 -0
- package/src/toolbars/hooks/Redo.js +33 -0
- package/src/toolbars/hooks/Ruby.js +59 -0
- package/src/toolbars/hooks/Size.js +100 -0
- package/src/toolbars/hooks/Split.js +37 -0
- package/src/toolbars/hooks/Strikethrough.js +65 -0
- package/src/toolbars/hooks/Sub.js +58 -0
- package/src/toolbars/hooks/Sup.js +58 -0
- package/src/toolbars/hooks/SwitchModel.js +78 -0
- package/src/toolbars/hooks/Table.js +56 -0
- package/src/toolbars/hooks/Toc.js +35 -0
- package/src/toolbars/hooks/TogglePreview.js +79 -0
- package/src/toolbars/hooks/Ul.js +41 -0
- package/src/toolbars/hooks/Underline.js +65 -0
- package/src/toolbars/hooks/Undo.js +30 -0
- package/src/toolbars/hooks/Video.js +53 -0
- package/src/utils/LazyLoadImg.js +341 -0
- package/src/utils/autoindent.js +58 -0
- package/src/utils/codeBlockContentHandler.js +351 -0
- package/src/utils/config.js +98 -0
- package/src/utils/copy.js +55 -0
- package/src/utils/dialog.js +196 -0
- package/src/utils/dom.js +162 -0
- package/src/utils/downloadUtil.js +23 -0
- package/src/utils/env.js +22 -0
- package/src/utils/error.js +61 -0
- package/src/utils/event.js +38 -0
- package/src/utils/export.js +115 -0
- package/src/utils/file.js +121 -0
- package/src/utils/formulaUtilsHandler.js +230 -0
- package/src/utils/htmlparser.js +977 -0
- package/src/utils/image.js +99 -0
- package/src/utils/imgSizeHandler.js +279 -0
- package/src/utils/jsonUtils.js +17 -0
- package/src/utils/lineFeed.js +49 -0
- package/src/utils/listContentHandler.js +227 -0
- package/src/utils/lookbehind-replace.js +81 -0
- package/src/utils/mathjax.js +89 -0
- package/src/utils/myersDiff.js +211 -0
- package/src/utils/pasteHelper.js +253 -0
- package/src/utils/recount-pos.js +59 -0
- package/src/utils/regexp.js +295 -0
- package/src/utils/sanitize.js +477 -0
- package/src/utils/selection.js +50 -0
- package/src/utils/svgUtils.js +96 -0
- package/src/utils/tableContentHandler.js +592 -0
- package/tools/README.md +3 -0
package/src/Sanitizer.js
ADDED
package/src/Stats.js
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import Event from '@/Event';
|
|
2
|
+
|
|
3
|
+
export default class Stats {
|
|
4
|
+
constructor(options) {
|
|
5
|
+
this.$cherry = options.$cherry;
|
|
6
|
+
console.log(options);
|
|
7
|
+
if (!this.$cherry.options.stats) return;
|
|
8
|
+
this.container = null;
|
|
9
|
+
this.stats = {
|
|
10
|
+
characters: 0,
|
|
11
|
+
words: 0,
|
|
12
|
+
paragraphs: 0,
|
|
13
|
+
};
|
|
14
|
+
this.init();
|
|
15
|
+
this.bindEvents();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
init() {
|
|
19
|
+
// 创建统计信息容器
|
|
20
|
+
this.container = document.createElement('div');
|
|
21
|
+
this.container.className = 'cherry-editor-stats';
|
|
22
|
+
const { editorDom } = this.$cherry.editor.options;
|
|
23
|
+
editorDom.appendChild(this.container);
|
|
24
|
+
this.update(this.$cherry.getValue());
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
bindEvents() {
|
|
28
|
+
const cm = this.$cherry.editor.editor; // 你得确认这里能拿到 CodeMirror 实例
|
|
29
|
+
cm.on('change', (instance, changeObj) => {
|
|
30
|
+
const addedText = changeObj.text.join('\n');
|
|
31
|
+
const removedText = (changeObj.removed || []).join('\n');
|
|
32
|
+
|
|
33
|
+
this.incrementalUpdate(removedText, addedText);
|
|
34
|
+
this.render();
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
incrementalUpdate(removed, added) {
|
|
39
|
+
const addedStats = this.wordCount(added);
|
|
40
|
+
const removedStats = this.wordCount(removed);
|
|
41
|
+
|
|
42
|
+
this.stats.characters += addedStats.characters - removedStats.characters;
|
|
43
|
+
this.stats.words += addedStats.words - removedStats.words;
|
|
44
|
+
this.stats.paragraphs += addedStats.paragraphs - removedStats.paragraphs;
|
|
45
|
+
|
|
46
|
+
Object.keys(this.stats).forEach((key) => {
|
|
47
|
+
this.stats[key] = Math.max(0, this.stats[key]);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
update(markdown) {
|
|
52
|
+
this.stats = this.wordCount(markdown);
|
|
53
|
+
this.render();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
render() {
|
|
57
|
+
if (this.container) {
|
|
58
|
+
this.container.innerHTML = `
|
|
59
|
+
<span class="cherry-stats-item">
|
|
60
|
+
<span class="material-symbols-outlined">counter_1</span>
|
|
61
|
+
${this.stats.characters} 字符
|
|
62
|
+
</span>
|
|
63
|
+
<span class="cherry-stats-item">
|
|
64
|
+
<span class="material-symbols-outlined">description</span>
|
|
65
|
+
${this.stats.words} 词
|
|
66
|
+
</span>
|
|
67
|
+
<span class="cherry-stats-item">
|
|
68
|
+
<span class="material-symbols-outlined">segment</span>
|
|
69
|
+
${this.stats.paragraphs} 段落
|
|
70
|
+
</span>
|
|
71
|
+
`;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
wordCount(markdown) {
|
|
76
|
+
// 匹配中文和标点符号
|
|
77
|
+
const pattern =
|
|
78
|
+
/[\u4e00-\u9fa5]|[\u3001\u3002\uff01\uff0c\uff1b\uff1a\u201c\u201d\u2018\u2019\u300a\u300b\u3008\u3009\u3010\u3011\u300e\u300f\u300c\u300d\uff08\uff09\u2014\u2026\u2013\uff0e]/g;
|
|
79
|
+
// 统计字符数量,排除换行和空格
|
|
80
|
+
const characters = markdown.replace(/\n|\s/g, '').length;
|
|
81
|
+
// 统计中文和标点符号
|
|
82
|
+
const chineseWords = (markdown.match(pattern) || []).length;
|
|
83
|
+
// 统计英文单词
|
|
84
|
+
const englishWords = markdown
|
|
85
|
+
.replace(pattern, ' ')
|
|
86
|
+
.split(/[\s\n]+/)
|
|
87
|
+
.filter(Boolean).length;
|
|
88
|
+
const words = chineseWords + englishWords;
|
|
89
|
+
// 统计段落数量,使用至少两个连续换行符分割段落
|
|
90
|
+
const paragraphs = markdown.split(/\n{2,}/).filter((line) => line.trim() !== '').length;
|
|
91
|
+
return { characters, words, paragraphs };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
destroy() {
|
|
95
|
+
if (this.container) {
|
|
96
|
+
Event.off('editor', 'change');
|
|
97
|
+
this.container.remove();
|
|
98
|
+
this.container = null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
package/src/Theme.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// 总共就两种颜色:light dark
|
|
2
|
+
import { changeTheme } from './utils/config';
|
|
3
|
+
import Event from './Event';
|
|
4
|
+
|
|
5
|
+
export const Theme = {
|
|
6
|
+
/**
|
|
7
|
+
* @param {Cherry} $cherry
|
|
8
|
+
*/
|
|
9
|
+
init($cherry) {
|
|
10
|
+
const savedTheme = localStorage.getItem('cherry-theme') || 'auto';
|
|
11
|
+
Theme.setTheme($cherry, savedTheme);
|
|
12
|
+
|
|
13
|
+
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
|
|
14
|
+
if (Theme.getTheme() === 'auto') {
|
|
15
|
+
const systemPrefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
16
|
+
Theme.applyTheme($cherry, systemPrefersDarkScheme ? 'dark' : 'light');
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
},
|
|
20
|
+
isDark() {
|
|
21
|
+
const theme = Theme.getTheme();
|
|
22
|
+
return theme === 'dark' || (theme === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches);
|
|
23
|
+
},
|
|
24
|
+
getTheme() {
|
|
25
|
+
return localStorage.getItem('cherry-theme') || 'auto';
|
|
26
|
+
},
|
|
27
|
+
setTheme($cherry, theme) {
|
|
28
|
+
localStorage.setItem('cherry-theme', theme);
|
|
29
|
+
if (theme === 'auto') {
|
|
30
|
+
const systemPrefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
31
|
+
Theme.applyTheme($cherry, systemPrefersDarkScheme ? 'dark' : 'light');
|
|
32
|
+
} else {
|
|
33
|
+
Theme.applyTheme($cherry, theme);
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
applyTheme($cherry, theme) {
|
|
37
|
+
changeTheme($cherry, theme);
|
|
38
|
+
document.querySelectorAll('.cherry').forEach(function (elem) {
|
|
39
|
+
elem.setAttribute('data-code-block-theme', theme);
|
|
40
|
+
});
|
|
41
|
+
document.querySelectorAll(`.cherry__theme__${theme === 'dark' ? 'light' : 'dark'}`).forEach(function (elem) {
|
|
42
|
+
elem.classList.replace(`cherry__theme__${theme === 'dark' ? 'light' : 'dark'}`, `cherry__theme__${theme}`);
|
|
43
|
+
});
|
|
44
|
+
Event.emit('Theme', 'change', theme === 'dark');
|
|
45
|
+
},
|
|
46
|
+
};
|
package/src/UrlCache.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (C) 2021 THL A29 Limited, a Tencent company.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import md5 from 'md5';
|
|
17
|
+
|
|
18
|
+
let urlCache = {};
|
|
19
|
+
const cherryInnerLinkRegex = /^cherry-inner:\/\/([0-9a-f]+)$/i;
|
|
20
|
+
|
|
21
|
+
export function urlProcessorProxy(urlProcessor) {
|
|
22
|
+
return function (url, srcType) {
|
|
23
|
+
if (UrlCache.isInnerLink(url)) {
|
|
24
|
+
const newUrl = urlProcessor(UrlCache.get(url), srcType);
|
|
25
|
+
return UrlCache.replace(url, newUrl);
|
|
26
|
+
}
|
|
27
|
+
return urlProcessor(url, srcType);
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export default class UrlCache {
|
|
32
|
+
/**
|
|
33
|
+
* 判断url是否Cherry的内部链接
|
|
34
|
+
* @param {string} url 要检测的URL
|
|
35
|
+
* @returns
|
|
36
|
+
*/
|
|
37
|
+
static isInnerLink(url) {
|
|
38
|
+
return cherryInnerLinkRegex.test(url);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 缓存url为内部链接,主要用于缩短超长链接,避免正则超时
|
|
43
|
+
* @param {string} url 要转换为内部链接的URL
|
|
44
|
+
* @returns
|
|
45
|
+
*/
|
|
46
|
+
static set(url) {
|
|
47
|
+
const urlSign = md5(url);
|
|
48
|
+
urlCache[urlSign] = url;
|
|
49
|
+
return `cherry-inner://${urlSign}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 获取原始链接
|
|
54
|
+
* @param {string} innerUrl 内部链接
|
|
55
|
+
* @returns
|
|
56
|
+
*/
|
|
57
|
+
static get(innerUrl) {
|
|
58
|
+
const [, urlSign] = innerUrl.match(cherryInnerLinkRegex) ?? [];
|
|
59
|
+
if (!urlSign) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
return urlCache[urlSign];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 替换指定内部链接的真实地址
|
|
67
|
+
* @param {string} innerUrl 原始内部链接
|
|
68
|
+
* @param {string} newUrl 需要替换的链接
|
|
69
|
+
*/
|
|
70
|
+
static replace(innerUrl, newUrl) {
|
|
71
|
+
const [, urlSign] = innerUrl.match(cherryInnerLinkRegex) ?? [];
|
|
72
|
+
if (!urlSign) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
urlCache[urlSign] = newUrl;
|
|
76
|
+
return innerUrl;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 替换所有内部链接为原始的真实地址
|
|
81
|
+
* @param {string} html 包含 cherry-inner 协议地址的 html 文本
|
|
82
|
+
*/
|
|
83
|
+
static restoreAll(html) {
|
|
84
|
+
const cherryInnerLinkRegex = /cherry-inner:\/\/([0-9a-f]+)/gi;
|
|
85
|
+
const $html = html.replace(cherryInnerLinkRegex, (match) => {
|
|
86
|
+
const originalUrl = UrlCache.get(match);
|
|
87
|
+
return originalUrl || match;
|
|
88
|
+
});
|
|
89
|
+
return $html;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* 清空缓存
|
|
94
|
+
*/
|
|
95
|
+
static clear() {
|
|
96
|
+
urlCache = {};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (C) 2021 THL A29 Limited, a Tencent company.
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import mergeWith from 'lodash/mergeWith';
|
|
18
|
+
export default class CardCodeEngine {
|
|
19
|
+
static install(cherryOptions, args) {
|
|
20
|
+
mergeWith(cherryOptions, {
|
|
21
|
+
engine: {
|
|
22
|
+
syntax: {
|
|
23
|
+
codeBlock: {
|
|
24
|
+
customRenderer: {
|
|
25
|
+
card: new CardCodeEngine(),
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
render(src, sign, $engine, config = {}) {
|
|
34
|
+
const parseCardSyntax = (text) => {
|
|
35
|
+
const lines = text.trim().split('\n');
|
|
36
|
+
const result = {
|
|
37
|
+
type: lines[0].startsWith('#image') ? 'image' : 'list',
|
|
38
|
+
count: 'auto',
|
|
39
|
+
data: []
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// 处理配置行
|
|
43
|
+
if (lines[0].startsWith('#')) {
|
|
44
|
+
const [type, count] = lines[0].slice(1).split('/');
|
|
45
|
+
result.type = type || 'list';
|
|
46
|
+
result.count = count ? parseInt(count) : 'auto';
|
|
47
|
+
lines.shift();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 解析每行
|
|
51
|
+
lines.forEach(line => {
|
|
52
|
+
line = line.trim();
|
|
53
|
+
if (!line) return;
|
|
54
|
+
|
|
55
|
+
// 匹配模式:[title](link) description
|
|
56
|
+
const regex = /(?:!\[(.*?)\]\((.*?)\))?\s*\[(.*?)\]\((.*?)\)(?:\s+(.*))?/;
|
|
57
|
+
const match = line.match(regex);
|
|
58
|
+
if (match) {
|
|
59
|
+
const [_, imgAlt, imgUrl, title, link, desc] = match;
|
|
60
|
+
result.data.push({
|
|
61
|
+
title: title || '',
|
|
62
|
+
desc: desc || '',
|
|
63
|
+
image: imgUrl || '',
|
|
64
|
+
link: link || '',
|
|
65
|
+
bgColor: '', // 支持默认值
|
|
66
|
+
textColor: '' // 支持默认值
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
return result;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
const json = parseCardSyntax(src);
|
|
76
|
+
let row = json.count;
|
|
77
|
+
if (row === 'auto') {
|
|
78
|
+
row = 3;
|
|
79
|
+
if (json.data.length < row) {
|
|
80
|
+
row = json.data.length;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (!row || row > 4 || row < 1) {
|
|
84
|
+
row = 3; // 默认 3
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const { type } = json;
|
|
88
|
+
let listDOM = ``;
|
|
89
|
+
if (type === 'list') {
|
|
90
|
+
// 普通卡片列表
|
|
91
|
+
listDOM = this.getListDOM(json.data, row);
|
|
92
|
+
} else if (type === 'image') {
|
|
93
|
+
// 卡片图片列表
|
|
94
|
+
listDOM = this.getImageDOM(json.data, row);
|
|
95
|
+
}
|
|
96
|
+
return `<div class="cherry-card cherry-card-${type}-container">${listDOM}</div>`;
|
|
97
|
+
} catch (e) {
|
|
98
|
+
console.warn('Card syntax parse error:', e);
|
|
99
|
+
return `<div class="cherry-card-error">卡片语法错误,请检查格式</div>`;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
getRandomColor() {
|
|
103
|
+
const colors = [
|
|
104
|
+
{ bg: '#34495E', text: '#BDC3C7' }, // 深蓝灰色背景,浅灰色文字
|
|
105
|
+
{ bg: '#16A085', text: '#A3E4D7' }, // 绿松石色背景,浅绿松石色文字
|
|
106
|
+
{ bg: '#27AE60', text: '#A9DFBF' }, // 绿色背景,浅绿色文字
|
|
107
|
+
{ bg: '#2980B9', text: '#AED6F1' }, // 蓝色背景,浅蓝色文字
|
|
108
|
+
{ bg: '#8E44AD', text: '#D2B4DE' }, // 紫色背景,浅紫色文字
|
|
109
|
+
{ bg: '#2C3E50', text: '#ECF0F1' }, // 深蓝灰色背景,浅灰色文字
|
|
110
|
+
{ bg: '#F39C12', text: '#FDEBD0' }, // 橙黄色背景,浅橙色文字
|
|
111
|
+
{ bg: '#D35400', text: '#F5CBA7' }, // 深橙色背景,浅橙色文字
|
|
112
|
+
{ bg: '#C0392B', text: '#F5B7B1' }, // 深红色背景,浅红色文字
|
|
113
|
+
{ bg: '#7F8C8D', text: '#D5DBDB' }, // 灰青色背景,浅灰色文字
|
|
114
|
+
{ bg: '#95A5A6', text: '#E5E8E8' }, // 浅青色背景,浅灰色文字
|
|
115
|
+
{ bg: '#F4D03F', text: '#FCF3CF' }, // 明黄色背景,浅黄色文字
|
|
116
|
+
{ bg: '#1ABC9C', text: '#A2D9CE' }, // 浅绿松石色背景,浅绿松石色文字
|
|
117
|
+
{ bg: '#3498DB', text: '#D6EAF8' }, // 浅蓝色背景,浅蓝色文字
|
|
118
|
+
{ bg: '#9B59B6', text: '#E8DAEF' }, // 浅紫色背景,浅紫色文字
|
|
119
|
+
{ bg: '#34495E', text: '#AAB7B8' }, // 蓝灰色背景,浅灰色文字
|
|
120
|
+
{ bg: '#E67E22', text: '#FAD7A0' }, // 橙色背景,浅橙色文字
|
|
121
|
+
{ bg: '#E74C3C', text: '#F5B7B1' }, // 红色背景,浅红色文字
|
|
122
|
+
{ bg: '#BDC3C7', text: '#2C3E50' }, // 浅灰色背景,深蓝灰色文字
|
|
123
|
+
{ bg: '#7F8C8D', text: '#2C3E50' }, // 灰青色背景,深蓝灰色文字
|
|
124
|
+
];
|
|
125
|
+
|
|
126
|
+
return colors[Math.floor(Math.random() * colors.length)];
|
|
127
|
+
}
|
|
128
|
+
getListDOM(dataList, row) {
|
|
129
|
+
let listDOM = '';
|
|
130
|
+
dataList.forEach((item) => {
|
|
131
|
+
const randomColor = this.getRandomColor();
|
|
132
|
+
let {
|
|
133
|
+
link = '',
|
|
134
|
+
image = '',
|
|
135
|
+
bgColor = randomColor.bg,
|
|
136
|
+
textColor = randomColor.text,
|
|
137
|
+
title = '',
|
|
138
|
+
desc = '',
|
|
139
|
+
} = item;
|
|
140
|
+
|
|
141
|
+
if (link === '') {
|
|
142
|
+
link = null;
|
|
143
|
+
}
|
|
144
|
+
if (image === '') {
|
|
145
|
+
image = null;
|
|
146
|
+
}
|
|
147
|
+
if (title === '') {
|
|
148
|
+
title = '';
|
|
149
|
+
}
|
|
150
|
+
if (desc === '') {
|
|
151
|
+
desc = '';
|
|
152
|
+
}
|
|
153
|
+
if (bgColor === '') {
|
|
154
|
+
bgColor = randomColor.bg;
|
|
155
|
+
}
|
|
156
|
+
if (textColor === '') {
|
|
157
|
+
textColor = randomColor.text;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
listDOM += `
|
|
161
|
+
<${link ? `a href="${link}" target="_blank"` : 'span'} class="cherry-card-item ${
|
|
162
|
+
row ? `cherry-card-row-${row}` : ''
|
|
163
|
+
}"
|
|
164
|
+
style="padding:0;background-color:${bgColor};color:${textColor};"
|
|
165
|
+
>
|
|
166
|
+
${image ? `<img src="${image}" class="cherry-card-image" alt="">` : ''}
|
|
167
|
+
<div class="cherry-card-body">
|
|
168
|
+
<p class="cherry-card-title">${title}</p>
|
|
169
|
+
<p class="cherry-card-desc">${desc}</p>
|
|
170
|
+
</div>
|
|
171
|
+
</${link ? 'a' : 'span'}>
|
|
172
|
+
`;
|
|
173
|
+
});
|
|
174
|
+
return listDOM;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
getImageDOM(dataList, row) {
|
|
178
|
+
let listDOM = '';
|
|
179
|
+
dataList.forEach((item) => {
|
|
180
|
+
let { link = '', image = '', title = '', desc = '' } = item;
|
|
181
|
+
|
|
182
|
+
if (link === '') {
|
|
183
|
+
link = null;
|
|
184
|
+
}
|
|
185
|
+
if (image === '') {
|
|
186
|
+
image = '';
|
|
187
|
+
}
|
|
188
|
+
if (title === '') {
|
|
189
|
+
title = '';
|
|
190
|
+
}
|
|
191
|
+
if (desc === '') {
|
|
192
|
+
desc = null;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
listDOM += `
|
|
196
|
+
<div class="cherry-card-item ${row ? `cherry-card-row-${row}` : ''}" >
|
|
197
|
+
<a href="${link}" target="_blank">
|
|
198
|
+
<div class="cherry-card-box-img">
|
|
199
|
+
<img src="${image}" class="cherry-card-image" style="object-fit: cover" alt="">
|
|
200
|
+
</div>
|
|
201
|
+
<div class="cherry-card-box-info">
|
|
202
|
+
<p class="cherry-card-title">${title}</p>
|
|
203
|
+
${desc ? `<p class="cherry-card-desc" >${desc}</p>` : ''}
|
|
204
|
+
</div>
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
</a>
|
|
208
|
+
</div>
|
|
209
|
+
`;
|
|
210
|
+
});
|
|
211
|
+
return listDOM;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (C) 2021 THL A29 Limited, a Tencent company.
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import mergeWith from 'lodash/mergeWith';
|
|
18
|
+
import Event from '../Event';
|
|
19
|
+
import extraJSON from '@/utils/jsonUtils';
|
|
20
|
+
export default class EChartsCodeEngine {
|
|
21
|
+
static install(cherryOptions, args) {
|
|
22
|
+
mergeWith(cherryOptions, {
|
|
23
|
+
engine: {
|
|
24
|
+
syntax: {
|
|
25
|
+
codeBlock: {
|
|
26
|
+
customRenderer: {
|
|
27
|
+
echarts: new EChartsCodeEngine({
|
|
28
|
+
...args,
|
|
29
|
+
...(cherryOptions.engine.syntax.echarts ?? {}),
|
|
30
|
+
}),
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
constructor(echartsOptions = {}) {
|
|
39
|
+
const { echarts, apiHost = 'https://echarts-api.vercel.app' } = echartsOptions;
|
|
40
|
+
this.api = !echarts && !window.echarts;
|
|
41
|
+
this.apiHost = apiHost;
|
|
42
|
+
this.echartsInstanceRef = echarts || window.echarts;
|
|
43
|
+
const that = this;
|
|
44
|
+
Event.on('previewer', 'beforeRenderDom', function ([sign, dom]) {
|
|
45
|
+
if (that.api) return;
|
|
46
|
+
const chartClazz = `echarts-${sign}`;
|
|
47
|
+
const echartsCanvas = dom.querySelectorAll(`.${chartClazz}`);
|
|
48
|
+
if (echartsCanvas) {
|
|
49
|
+
for (const echartsCanva of echartsCanvas) {
|
|
50
|
+
const echart = that.echartsInstanceRef.getInstanceByDom(echartsCanva);
|
|
51
|
+
if (echart) {
|
|
52
|
+
sessionStorage.setItem('chartWidth', echart.getWidth());
|
|
53
|
+
sessionStorage.setItem('chartHeight', echart.getHeight());
|
|
54
|
+
echart.dispose();
|
|
55
|
+
sessionStorage.removeItem(chartClazz);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
Event.on('previewer', 'afterRenderDom', function ([sign, dom]) {
|
|
61
|
+
if (that.api) return;
|
|
62
|
+
const chartClazz = `echarts-${sign}`;
|
|
63
|
+
const item = sessionStorage.getItem(chartClazz);
|
|
64
|
+
if (!item) return;
|
|
65
|
+
const json = JSON.parse(item);
|
|
66
|
+
const echartsCanvas = document.querySelectorAll(`.${chartClazz}`);
|
|
67
|
+
if (!echartsCanvas) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
for (const echartsCanva of echartsCanvas) {
|
|
71
|
+
let myChart = that.echartsInstanceRef.getInstanceByDom(echartsCanva);
|
|
72
|
+
if (!myChart) {
|
|
73
|
+
myChart = that.echartsInstanceRef.init(
|
|
74
|
+
echartsCanva,
|
|
75
|
+
window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : '',
|
|
76
|
+
{
|
|
77
|
+
renderer: 'svg',
|
|
78
|
+
},
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
myChart.setOption(json);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Event.off('previewer', 'afterRenderDom', afterRender);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
function renderChart(isDark) {
|
|
88
|
+
const query = document.querySelectorAll('.echart-container');
|
|
89
|
+
if (!query) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
for (const queryElement of query) {
|
|
93
|
+
if (that.api) {
|
|
94
|
+
if (isDark) {
|
|
95
|
+
queryElement.src = queryElement.src.replace('theme%22%3A%22%22', 'theme%22%3A%22dark%22');
|
|
96
|
+
} else {
|
|
97
|
+
queryElement.src = queryElement.src.replace('theme%22%3A%22dark%22', 'theme%22%3A%22%22');
|
|
98
|
+
}
|
|
99
|
+
} else {
|
|
100
|
+
const item = sessionStorage.getItem(queryElement.dataset.json);
|
|
101
|
+
if (!item) continue;
|
|
102
|
+
const json = JSON.parse(item);
|
|
103
|
+
let myChart = that.echartsInstanceRef.getInstanceByDom(queryElement);
|
|
104
|
+
if (myChart) {
|
|
105
|
+
myChart.dispose();
|
|
106
|
+
}
|
|
107
|
+
myChart = that.echartsInstanceRef.init(queryElement, isDark ? 'dark' : '', {
|
|
108
|
+
renderer: 'svg',
|
|
109
|
+
});
|
|
110
|
+
myChart.setOption(json);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
window.addEventListener('resize', function () {
|
|
116
|
+
if (that.api) return;
|
|
117
|
+
const query = document.querySelectorAll('.echart-container');
|
|
118
|
+
if (!query) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
for (const queryElement of query) {
|
|
122
|
+
const myChart = that.echartsInstanceRef.getInstanceByDom(queryElement);
|
|
123
|
+
if (myChart) {
|
|
124
|
+
myChart.resize();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
Event.on('Theme', 'change', function ([isDark]) {
|
|
129
|
+
renderChart(isDark);
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
renderFromApi(json, isDark) {
|
|
134
|
+
const data = {
|
|
135
|
+
theme: isDark ? 'dark' : '',
|
|
136
|
+
width: 600,
|
|
137
|
+
height: 400,
|
|
138
|
+
options: json,
|
|
139
|
+
};
|
|
140
|
+
return `<img class='echart-container' style="max-width: 100%" src='${this.apiHost}?data=${encodeURIComponent(
|
|
141
|
+
JSON.stringify(data),
|
|
142
|
+
)}' alt=""/>`;
|
|
143
|
+
}
|
|
144
|
+
render(src, sign, $engine, config = {}) {
|
|
145
|
+
let $sign = sign;
|
|
146
|
+
if (!$sign) {
|
|
147
|
+
$sign = Math.round(Math.random() * 100000000);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const graphId = `echarts-${$sign}`;
|
|
151
|
+
|
|
152
|
+
const json = extraJSON(src);
|
|
153
|
+
|
|
154
|
+
if (this.api) {
|
|
155
|
+
return this.renderFromApi(json, window.matchMedia('(prefers-color-scheme: dark)').matches);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
sessionStorage.setItem(graphId, JSON.stringify(json));
|
|
159
|
+
return `<div class="${graphId} echart-container" data-json="${graphId}" style="height: 400px;width: 100%"></div>`;
|
|
160
|
+
}
|
|
161
|
+
}
|