diff --git a/package.json b/package.json index f9e05fc11..8d128f563 100644 --- a/package.json +++ b/package.json @@ -141,6 +141,7 @@ "dependencies": { "@types/codemirror": "^0.0.108", "@types/dompurify": "^2.2.3", + "cm-search-replace": "^1.0.0", "jsdom": "~19.0.0", "mitt": "^3.0.0" }, diff --git a/src/Editor.js b/src/Editor.js index 9de79d128..b1189c607 100644 --- a/src/Editor.js +++ b/src/Editor.js @@ -26,6 +26,10 @@ import 'codemirror/addon/edit/matchtags'; import 'codemirror/addon/search/searchcursor'; import 'codemirror/addon/display/placeholder'; import 'codemirror/keymap/sublime'; + +import 'cm-search-replace/src/search'; +import 'codemirror/addon/scroll/annotatescrollbar'; +import 'codemirror/addon/search/matchesonscrollbar'; // import 'codemirror/addon/selection/active-line'; // import 'codemirror/addon/edit/matchbrackets'; import htmlParser from '@/utils/htmlparser'; diff --git a/src/Previewer.js b/src/Previewer.js index 3282c6708..3f9a2d384 100644 --- a/src/Previewer.js +++ b/src/Previewer.js @@ -817,6 +817,8 @@ export default class Previewer { return scrollTo; } } + // 如果计算完预览区域所有的行号依然<左侧光标所在的行号,则预览区域直接滚到最低部 + return domContainer.scrollHeight; } /** diff --git a/src/core/hooks/Table.js b/src/core/hooks/Table.js index c308784c8..e47cd8cee 100644 --- a/src/core/hooks/Table.js +++ b/src/core/hooks/Table.js @@ -174,8 +174,23 @@ export default class Table extends ParagraphBase { }; } + /** + * 如果table.head是空的,就不渲染了 + * @param {String} str + * @returns {Boolean} + */ + $testHeadEmpty(str) { + const test = str + .replace(/ /g, '') + .replace(/\s/g, '') + .replace(/(~CTH\$|~CTHU|~CTHL|~CTHR|~CTHC)/g, ''); + return test?.length > 0; + } + $renderTable(COLUMN_ALIGN_MAP, tableHeader, tableRows, dataLines) { - const cacheSrc = `~CTHD${tableHeader}~CTHD$~CTBD${tableRows}~CTBD$`; + const cacheSrc = this.$testHeadEmpty(tableHeader) + ? `~CTHD${tableHeader}~CTHD$~CTBD${tableRows}~CTBD$` + : `~CTBD${tableRows}~CTBD$`; const html = cacheSrc; const sign = this.$engine.md5(html); const renderHtml = html diff --git a/src/sass/cherry.scss b/src/sass/cherry.scss index 9495c8a6c..1b04d429e 100644 --- a/src/sass/cherry.scss +++ b/src/sass/cherry.scss @@ -242,6 +242,13 @@ } } + +.cherry { + .ace_search { + background: #FFF; + } +} + .cherry-sidebar { width: 30px; position: absolute; diff --git a/src/utils/htmlparser.js b/src/utils/htmlparser.js index e8f310303..0122705ed 100644 --- a/src/utils/htmlparser.js +++ b/src/utils/htmlparser.js @@ -43,6 +43,7 @@ const htmlParser = { .replace(/\n{3,}/g, '\n\n\n') .replace(/>/g, '>') .replace(/</g, '<') + .replace(/&/g, '&') .trim('\n'); }, /** @@ -68,43 +69,7 @@ const htmlParser = { */ $handleTagObject(temObj, returnString) { let ret = returnString; - if (temObj.attrs.class && temObj.attrs.class.indexOf('mermaid') >= 0) { - // 针对 mermaid 图 - try { - ret += [ - '\n```', - temObj.attrs['data-type'], - '\n', - decodeURIComponent(atob(temObj.attrs['data-code'])), - '\n```\n', - ].join(''); - } catch (e) { - ret += [ - '\n```', - temObj.attrs['data-type'], - '\n', - decodeURIComponent(temObj.attrs['data-code']), - '\n```\n', - ].join(''); - } - } else if (temObj.attrs.class && temObj.attrs.class.indexOf('mathjax-wrapper') >= 0) { - // 针对公式,首尾加空格是为了适应新语法 - try { - ret += ` ${decodeURIComponent(atob(temObj.attrs['data-source']))} `; - } catch (e) { - ret += ` ${decodeURIComponent(temObj.attrs['data-source'])} `; - } - } else if (temObj.attrs['data-control'] && temObj.attrs['data-control'] === 'tapd-table') { - // 处理高阶表格 cherryMarkdown里不需要这段逻辑 - ret += ['\n```', ' tapd-table ', temObj.attrs['data-size'], '\n'].join(''); - - if (temObj.children[1] && temObj.children[1].children[0].content) { - const data = temObj.children[1].children[0].content.replace(/\s+/g, ''); - ret += ['\n', data, '\n```\n'].join(''); - } else { - ret += ['\n', '"工作表":{"数据":{"19::25":" "}} ', '\n```\n'].join(''); // 高阶数据存在问题,转换为默认 - } - } else if (temObj.attrs.class && temObj.attrs.class.indexOf('ch-icon') >= 0) { + if (temObj.attrs.class && temObj.attrs.class.indexOf('ch-icon') >= 0) { // 针对checklist if (temObj.attrs.class.indexOf('ch-icon-check') >= 0) { ret += '[x]'; @@ -168,8 +133,7 @@ const htmlParser = { // 递归找到对应的代码文本 ret += self.$dealCodeTag(temObj); } else { - // 代码块会对<、>做转义,转成markdown时就不需要转义了 - ret += temObj.content.replace(/</g, '<').replace(/>/g, '>'); + ret += temObj.content; } } return ret; @@ -761,7 +725,7 @@ const htmlParser = { }, convertCode(str) { if (/\n/.test(str)) { - return `\n\`\`\`\n${str.replace(/\n+$/, '')}\n\`\`\`\n`; + return `\`\`\`\n${str.replace(/\n+$/, '')}\n\`\`\``; } return ` \`${str.replace(/`/g, '\\`')}\` `; }, @@ -818,19 +782,19 @@ const htmlParser = { return `^^${str.trim().replace(/\^\^/g, '\\^\\^')}^^`; }, convertTd(str) { - return `~|${str.trim().replace(/\n/g, '
')} ~|`; + return `~|${str.trim().replace(/\n{1,}/g, '
')} ~|`; }, convertTh(str) { - return `~|${str.trim().replace(/\n/g, '
')} ~|`; + return `~|${str.trim().replace(/\n{1,}/g, '
')} ~|`; }, convertTr(str) { - return `${str}\n`; + return `${str.replace(/\n/g, '')}\n`; }, convertThead(str) { return `${str.replace(/~\|~\|/g, '~|').replace(/~\|/g, '|')}|:--|\n`; }, convertTable(str) { - const ret = `\n${str.replace(/~\|~\|/g, '~|').replace(/~\|/g, '|')}\n`; + const ret = `\n${str.replace(/~\|~\|/g, '~|').replace(/~\|/g, '|')}\n`.replace(/\n{2,}/g, '\n'); if (/\|:--\|/.test(ret)) { return ret; } @@ -840,7 +804,7 @@ const htmlParser = { return `- ${str.replace(/^\n/, '').replace(/\n+$/, '').replace(/\n+/g, '\n\t')}\n`; }, convertUl(str) { - return `\n\n${str}\n\n`; + return `${str}\n`; }, convertOl(str) { const arr = str.split('\n'); @@ -852,7 +816,7 @@ const htmlParser = { } } const $str = arr.join('\n'); - return `\n\n${$str}\n\n`; + return `${$str}\n`; }, convertStrong(str) { return /^\s*$/.test(str) ? '' : `**${str}**`; @@ -885,10 +849,10 @@ const htmlParser = { return `###### ${str.trim().replace(/\n+$/, '')}\n\n`; }, convertBlockquote(str) { - return `\n>${str.trim()}\n\n`; + return `>${str.trim()}\n\n`; }, convertAddress(str) { - return `\n>${str.trim()}\n\n`; + return `>${str.trim()}\n\n`; }, }, /**