diff --git a/plugin/custom/plugins/markdownLint/index.js b/plugin/custom/plugins/markdownLint/index.js
index 8270968d..f1d85831 100644
--- a/plugin/custom/plugins/markdownLint/index.js
+++ b/plugin/custom/plugins/markdownLint/index.js
@@ -16,8 +16,8 @@ class markdownLintPlugin extends BaseCustomPlugin {
-
+
@@ -47,13 +47,24 @@ class markdownLintPlugin extends BaseCustomPlugin {
const _getDetail = (infos = this.errors) => {
const obj = infos.map(i => this.utils.fromObject(i, ["lineNumber", "ruleNames", "errorDetail", "errorContext", "errorRange", "fixInfo"]));
const content = JSON.stringify(obj.length === 1 ? obj[0] : obj, null, "\t");
- const components = [{ label: "详细信息", type: "textarea", rows: 15, readonly: "readonly", content }];
- this.utils.modal({ title: "格式规范检测", width: "550px", components });
+ const components = [{ label: "", type: "textarea", rows: 15, readonly: "readonly", content }];
+ this.utils.modal({ title: "详细信息", width: "550px", components });
+ }
+ const _showDoc = () => {
+ const url = "https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md";
+ const onclick = ev => ev.target.closest("a") && this.utils.openUrl(url);
+ const content = JSON.stringify(this.l10n, null, "\t");
+ const components = [
+ { label: "以下为简单说明,如需查看完整文档请 前往网站", type: "p", onclick },
+ { label: "", type: "textarea", rows: 15, readonly: "readonly", content },
+ ];
+ this.utils.dialog.modal({ title: "格式规范", width: "600px", components });
}
+
const _funcMap = {
close: () => this.callback(),
refresh: () => this.checkLintError(),
- doc: () => this.utils.openUrl("https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md"),
+ doc: () => _showDoc(),
toggleSourceMode: () => File.toggleSourceMode(),
translate: () => {
this.config.translate = !this.config.translate;
@@ -61,16 +72,13 @@ class markdownLintPlugin extends BaseCustomPlugin {
},
settings: () => {
const content = JSON.stringify(this.config.rule_config, null, "\t");
- const components = [{ label: "当前配置", type: "textarea", rows: 15, readonly: "readonly", content }];
- this.utils.modal({ title: "格式规范检测", width: "550px", components });
+ const components = [{ label: "", type: "textarea", rows: 15, readonly: "readonly", content }];
+ this.utils.modal({ title: "当前配置", width: "550px", components });
},
detailAll: () => _getDetail(this.errors),
detailSingle: infoIdx => _getDetail([this.errors[infoIdx]]),
fixAll: () => this.fixLintError(),
- fixSingle: infoIdx => {
- const errors = [this.errors[infoIdx]];
- this.fixLintError(errors);
- },
+ fixSingle: infoIdx => this.fixLintError([this.errors[infoIdx]]),
jumpToLine: lineToGo => {
if (!lineToGo) return;
if (!File.editor.sourceView.inSourceMode) {
diff --git a/plugin/custom/plugins/markdownLint/l10n.js b/plugin/custom/plugins/markdownLint/l10n.js
index 0d6613e4..38c79460 100644
--- a/plugin/custom/plugins/markdownLint/l10n.js
+++ b/plugin/custom/plugins/markdownLint/l10n.js
@@ -1,36 +1,36 @@
module.exports = {
- MD001: "标题级别应该逐级递增,不允许跳级",
+ MD001: "标题级别应该逐级递增,不可跳级",
MD002: "第一个标题应该是顶级标题",
- MD003: "在标题前加#号来表示标题级别",
- MD004: "要求采用一致的无序列表的格式",
+ MD003: "在标题前加#号以表示标题级别",
+ MD004: "要求采用一致的无序列表格式",
MD005: "要求同级列表项的缩进是一致的",
MD006: "最高级标题不能缩进",
- MD007: "无序列表嵌套时,使用两个空格缩进",
+ MD007: "嵌套的无序列表要使用两个空格缩进",
MD008: "MD008",
- MD009: "行尾最多可以添加两个空格,用于表示换行",
+ MD009: "行尾最多空两格,以示换行",
MD010: "不能使用tab缩进,要使用空格",
- MD011: "内联形式的链接的中括号和圆括号使用错误",
+ MD011: "内联链接的中括号和圆括号使用错误",
MD012: "不能有连续的空行",
MD013: "行的长度应该在一定范围内",
- MD014: "代码块中,终端命令除非后接其输出,否则前面不能有$符号",
+ MD014: "代码块中的终端命令除非后接其输出,否则不能前接$号",
MD015: "MD015",
MD016: "MD016",
MD017: "MD017",
- MD018: "atx标题格式下,#号和文字之间需要一个空格隔开",
- MD019: "atx标题格式下,#号和文字之间的空格不能多于一个",
- MD020: "closed_atx标题格式下,文字和前后#号之间需用一个空格隔开",
- MD021: "closed_atx标题格式下,文字和前后#号之间的空格不能多于一个",
- MD022: "标题行的上下行应该都是空行",
+ MD018: "atx标题格式下,#号和文字之间需空一格",
+ MD019: "atx标题格式下,#号和文字之间只能空一格",
+ MD020: "closed_atx标题格式下,文字和#号之间需空一格",
+ MD021: "closed_atx标题格式下,文字和#号之间只能空一格",
+ MD022: "标题行的前后需要用空行隔开",
MD023: "标题行不能缩进",
MD024: "不能连续出现内容重复的标题",
MD025: "只能有一个一级标题",
MD026: "标题不应以标点符号结尾",
- MD027: "引用区块的引用符号和文字之间有且只有一个空格",
- MD028: "两个引用区块间不能用空行隔开。引用区块中的空行要用>开头",
- MD029: "要求有序列表的序号从1开始,按顺序递增",
- MD030: "列表的每一列表项的标识符后只能空一格,后接列表内容",
- MD031: "单独的代码块前后需要用空行隔开",
- MD032: "列表前后需要用空行隔开,列表的缩进必须一致",
+ MD027: "引用区块的引用符号和文字之间需空一格",
+ MD028: "引用区块之间的空行要以>开头",
+ MD029: "要求有序列表的序号从1开始,按序递增",
+ MD030: "列表项标识符后只能空一格,后接列表内容",
+ MD031: "单独的代码块前后需用空行隔开",
+ MD032: "列表前后需用空行隔开,列表的缩进必须一致",
MD033: "不建议使用HTML语句",
MD034: "单纯的链接地址需要用尖括号包裹",
MD035: "要求采用一致的水平线格式",
@@ -39,9 +39,9 @@ module.exports = {
MD038: "反引号的内侧不应紧邻空格",
MD039: "链接中,中括号的内侧不应紧邻空格",
MD040: "代码块应该指定编程语言",
- MD041: "文档正文一开始必须是一级标题",
+ MD041: "文档正文必须以一级标题开始",
MD042: "链接的地址不能为空",
- MD043: "要求标题遵循一定的结构",
+ MD043: "要求标题遵循约定的结构",
MD044: "大小写错误",
MD045: "图片链接必须包含描述文本",
MD046: "代码块要用三个反引号包裹",
@@ -49,12 +49,12 @@ module.exports = {
MD048: "要求采用一致的代码块分隔符",
MD049: "要求采用一致的斜体格式",
MD050: "要求采用一致的加粗格式",
- MD051: "文内链接必须有效,不能指向一个不存在的标题",
- MD052: "引用链接和图片应该使用已经定义的标签",
+ MD051: "文内链接必须有效",
+ MD052: "引用链接和图片应使用已经定义的标签",
MD053: "链接和图片引用定义不可省略",
MD054: "要求采用一致的链接和图片格式",
MD055: "要求采用一致的表格分隔符格式",
- MD056: "表格列数要求是一致的,不能省略或多余",
+ MD056: "表格列数必须一致的",
MD057: "MD057",
MD058: "表格前后需要用空行隔开",
};
\ No newline at end of file
diff --git a/plugin/custom/plugins/resourceOperation.js b/plugin/custom/plugins/resourceOperation.js
index 61d808b9..b6bf3c91 100644
--- a/plugin/custom/plugins/resourceOperation.js
+++ b/plugin/custom/plugins/resourceOperation.js
@@ -42,12 +42,12 @@ class resourceOperationPlugin extends BaseCustomPlugin {
this.regexp = ignore_img_html_element
? new RegExp("!\\[.*?\\]\\((?.*)\\)", "g")
: new RegExp("!\\[.*?\\]\\((?.*)\\)|.*?)\"", "g")
- this.resourceSuffix = new Set(resource_suffix);
- this.fileSuffix = new Set(markdown_suffix);
+ this.resourceSuffixs = new Set(resource_suffix);
+ this.fileSuffixs = new Set(markdown_suffix);
this.resources = new Set();
this.resourcesInFile = new Set();
if (collect_file_without_suffix) {
- this.resourceSuffix.add("");
+ this.resourceSuffixs.add("");
}
this.nonExistInFile = null;
this.nonExistInFolder = null;
@@ -77,10 +77,10 @@ class resourceOperationPlugin extends BaseCustomPlugin {
if (!target) return;
const tr = target.closest("tr");
if (!tr) return;
- const p = tr.querySelector(".plugin-resource-operation-src");
- if (!p) return;
+ const img = tr.querySelector("img");
+ if (!img) return;
- const src = p.dataset.path;
+ const src = img.getAttribute("src");
const action = target.getAttribute("action");
if (action === "delete") {
if (this.showWarnDialog) {
@@ -126,21 +126,22 @@ class resourceOperationPlugin extends BaseCustomPlugin {
delete output.resource_non_exist_in_file;
delete output.resource_non_exist_in_folder;
const replacer = (key, value) => Array.isArray(value) ? value.join("|") : value
+
const btnGroup = ` | `
- const rows = Array.from(this.nonExistInFile, (row, idx) => `${idx + 1} | ${row} | ${btnGroup}
`)
+ const nonExistInFile = Array.from(this.nonExistInFile, (row, idx) => `${idx + 1} | ${row} | | ${btnGroup}
`)
+ const nonExistInFolder = Array.from(this.nonExistInFolder, (row, idx) => `${idx + 1} | ${row} |
`)
+
this.entities.wrap.innerHTML = `
-
+
存在于文件夹但不存在于md文件的资源(共${this.nonExistInFile.size}项)
- # | resource | operation |
- ${rows.join("")}
+ # | resource | preview | operation |
+ ${nonExistInFile.join("")}
-
存在于md文件但不存在于文件夹的资源(共${this.nonExistInFolder.size}项)
# | resource |
- ${Array.from(this.nonExistInFolder, (row, idx) => `${idx + 1} | ${row} |
`).join("")}
+ ${nonExistInFolder.join("")}
-
配置
`
@@ -157,8 +158,10 @@ class resourceOperationPlugin extends BaseCustomPlugin {
togglePreview = force => {
const icon = this.entities.iconGroup.querySelector('[action="togglePreview"]');
const wantClose = force === false || icon.classList.contains("ion-eye");
+ this.entities.wrap.querySelectorAll(".non-exist-in-file-table td:nth-of-type(3), .non-exist-in-file-table th:nth-of-type(3)")
+ .forEach(e => e.classList.toggle("plugin-common-hidden", wantClose));
const func = wantClose ? "off" : "on";
- const className = ".plugin-resource-operation-src";
+ const className = "img";
this.entities.$wrap
[func]("mouseover", className, this._showPopup)
[func]("mouseout", className, this._hidePopup)
@@ -171,7 +174,7 @@ class resourceOperationPlugin extends BaseCustomPlugin {
const popup = this.entities.popup;
if (!popup) return;
- popup.src = ev.target.dataset.path;
+ popup.src = ev.target.getAttribute("src");
const left = Math.min(window.innerWidth - 10 - popup.offsetWidth, ev.clientX + 10);
const top = Math.min(window.innerHeight - 50 - popup.offsetHeight, ev.clientY + 20);
popup.style.left = `${left}px`;
@@ -182,8 +185,8 @@ class resourceOperationPlugin extends BaseCustomPlugin {
getOutput = () => ({
search_folder: this.utils.getMountFolder(),
- resource_suffix: Array.from(this.resourceSuffix),
- markdown_suffix: Array.from(this.fileSuffix),
+ resource_suffix: Array.from(this.resourceSuffixs),
+ markdown_suffix: Array.from(this.fileSuffixs),
ignore_img_html_element: this.config.ignore_img_html_element,
collect_file_without_suffix: this.config.collect_file_without_suffix,
ignore_folders: this.config.ignore_folders,
@@ -265,40 +268,39 @@ Designed with ♥ by [obgnail](https://github.com/obgnail/typora_plugin)
return imagePath;
}
- const collectMatch = async content => {
- for (const match of content.matchAll(this.regexp)) {
+ const collectMatch = content => {
+ return Promise.all(content.matchAll(this.regexp).map(async match => {
let src = match.groups.src1 || match.groups.src2;
- if (!src || isNetworkImage(src) || isSpecialImage(src)) continue;
+ if (!src || isNetworkImage(src) || isSpecialImage(src)) return;
try {
- src = decodeURI(src).split("?")[0];
+ src = decodeURIComponent(src).split("?")[0];
} catch (e) {
console.warn("error path:", src);
- continue
+ return;
}
src = resolve(dir, src);
- if (this.resourcesInFile.has(src)) continue;
+ if (this.resourcesInFile.has(src)) return;
const resourcePath = await getRealPath(src);
- this.resourcesInFile.add(resourcePath);
-
+ if (this.resourceSuffixs.has(extname(resourcePath).toLowerCase())) {
+ this.resourcesInFile.add(resourcePath);
+ }
const remain = src.slice(resourcePath.length);
if (remain) {
await collectMatch(remain + ")");
}
- }
+ }))
}
const ext = extname(filePath).toLowerCase();
- if (this.resourceSuffix.has(ext)) {
+ if (this.resourceSuffixs.has(ext)) {
this.resources.add(filePath);
- return
+ } else if (this.fileSuffixs.has(ext)) {
+ const buffer = await readFile(filePath);
+ await collectMatch(buffer.toString());
}
- if (!this.fileSuffix.has(ext)) return;
-
- const buffer = await readFile(filePath);
- await collectMatch(buffer.toString());
}
traverseDir = async (dir, callback) => {
@@ -307,15 +309,15 @@ Designed with ♥ by [obgnail](https://github.com/obgnail/typora_plugin)
async function traverse(dir) {
const files = await readdir(dir);
- for (const file of files) {
+ await Promise.all(files.map(async file => {
const filePath = join(dir, file);
const stats = await stat(filePath);
if (stats.isFile()) {
- await callback(filePath, dir)
+ await callback(filePath, dir);
} else if (stats.isDirectory() && !ignore_folders.includes(file)) {
await traverse(filePath);
}
- }
+ }))
}
await traverse(dir);
diff --git a/plugin/file_counter.js b/plugin/file_counter.js
index d317a6c5..30c1dac8 100644
--- a/plugin/file_counter.js
+++ b/plugin/file_counter.js
@@ -65,11 +65,10 @@ class fileCounterPlugin extends BasePlugin {
async function traverse(dir) {
const stats = await promises.stat(dir);
- if (!stats.isDirectory()) {
- return;
- }
+ if (!stats.isDirectory()) return;
+
const files = await promises.readdir(dir);
- for (const file of files) {
+ await Promise.all(files.map(async file => {
const filePath = Path.join(dir, file);
const fileStats = await promises.stat(filePath);
if (fileStats.isFile() && fileFilter(filePath, fileStats)) {
@@ -78,7 +77,7 @@ class fileCounterPlugin extends BasePlugin {
if (fileStats.isDirectory() && dirFilter(file)) {
await traverse(filePath);
}
- }
+ }))
}
traverse(dir).then(() => then(fileCount)).catch(err => console.error(err));
diff --git a/plugin/global/styles/resourceOperation.css b/plugin/global/styles/resourceOperation.css
index b76a59c0..f12ba0fa 100644
--- a/plugin/global/styles/resourceOperation.css
+++ b/plugin/global/styles/resourceOperation.css
@@ -12,8 +12,8 @@
}
#plugin-resource-operation .plugin-resource-operation-popup {
- max-width: 400px;
- max-height: 400px;
+ max-width: 500px;
+ max-height: 500px;
position: fixed;
box-shadow: 0 0 0.125em 0 rgba(0, 0, 0, .5);
border-radius: 0.5em;
@@ -21,7 +21,26 @@
z-index: 9999;
}
-#plugin-resource-operation td {
+.non-exist-in-file-table th:first-child {
+ width: 10px;
+}
+.non-exist-in-file-table th:last-child {
+ width: 150px;
+}
+.non-exist-in-file-table td:nth-of-type(2) {
+ max-width: 200px;
+ white-space: break-spaces;
+ word-break: break-word;
+}
+.non-exist-in-file-table td:nth-of-type(3) {
+ max-width: 100px;
+ max-height: 100px;
+}
+.non-exist-in-file-table td:last-of-type {
+ text-align: center;
+}
+
+#plugin-resource-operation tbody {
font-size: 0.7em;
}
diff --git a/plugin/search_multi.js b/plugin/search_multi.js
index bd2ae589..e3ba0725 100644
--- a/plugin/search_multi.js
+++ b/plugin/search_multi.js
@@ -140,15 +140,16 @@ class searchMultiKeywordPlugin extends BasePlugin {
async function traverse(dir) {
const files = await readdir(dir);
- for (const file of files) {
+ await Promise.all(files.map(async file => {
const filePath = Path.join(dir, file);
const stats = await stat(filePath);
if (stats.isFile() && (!fileFilter || fileFilter(filePath, stats))) {
- readFile(filePath).then(buffer => callback(filePath, stats, buffer)).catch(console.error);
+ const buffer = await readFile(filePath);
+ callback(filePath, stats, buffer);
} else if (stats.isDirectory() && (!dirFilter || dirFilter(file))) {
await traverse(filePath);
}
- }
+ }))
}
traverse(dir).then(then).catch(console.error);