diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 8a4472d1..089e183a 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -6,6 +6,25 @@ This update **fixes a XSS security vulnerability** when exporting a document. - Minimum supported macOS version is 10.10 (Yosemite) - Remove `lightColor` and `darkColor` in user preference (color change in view menu does not work any, and will remove when add custom theme.) +- We recommand user not use block element in paragraph, please use block element in html block. + +*Not Recommand* + +```md +foo
bar
zar +``` + +*Recommand* + +```md +
+ foo +
+ bar +
+ zar +
+``` **:cactus:Feature** @@ -21,6 +40,7 @@ This update **fixes a XSS security vulnerability** when exporting a document. - Support maxOS `dark mode`, when you change `mode dark or light` in system, Mark Text will change its theme. - Add new themes: Ulysses Light, Graphite Light, Material Dark and One Dark. - Watch file changed in tabs and show a notice(autoSave is `false`) or update the file(autoSave is `true`) +- Support input inline Ruby charactors as raw html (#257) **:butterfly:Optimization** @@ -38,6 +58,7 @@ This update **fixes a XSS security vulnerability** when exporting a document. - Make table of contents in sidebar collapsible (#404) - Hide titlebar control buttons in custom titlebar style - Corrected hamburger menu offset +- Optimization of inline html displa, now you can nest other inline syntax in inline html(#849) **:beetle:Bug fix** diff --git a/src/muya/lib/assets/styles/index.css b/src/muya/lib/assets/styles/index.css index 252de0ba..4fd5fd8c 100644 --- a/src/muya/lib/assets/styles/index.css +++ b/src/muya/lib/assets/styles/index.css @@ -117,6 +117,10 @@ span.ag-html-tag { font-family: monospace; } +span.ag-ruby { + position: relative; + vertical-align: bottom; +} span.ag-math { position: relative; color: var(--editorColor); @@ -125,7 +129,8 @@ span.ag-math { vertical-align: bottom; } -.ag-math > .ag-math-render { +.ag-math > .ag-math-render, +.ag-ruby > .ag-ruby-render { display: inline-block; padding: .5rem; border-radius: 4px; @@ -135,6 +140,12 @@ span.ag-math { z-index: 1; } +.ag-ruby > .ag-ruby-render { + padding-bottom: 0; + left: 50%; + transform: translateX(-50%); +} + div.ag-empty { text-align: center; color: var(--editorColor50); @@ -163,18 +174,19 @@ span.ag-math > .ag-math-render.ag-math-error { white-space: nowrap; } +.ag-hide.ag-ruby, .ag-hide.ag-math { width: auto; height: auto; } - +.ag-hide.ag-ruby > .ag-ruby-text, .ag-hide.ag-math > .ag-math-text { display: inline-block; width: 0; height: 0; overflow: hidden; } - +.ag-hide.ag-ruby > .ag-ruby-render, .ag-hide.ag-math > .ag-math-render { padding: 0; top: 0; @@ -184,6 +196,7 @@ span.ag-math > .ag-math-render.ag-math-error { background: transparent; } +.ag-gray.ag-ruby > .ag-ruby-render::before .ag-gray.ag-math > .ag-math-render::before { border-width: 5px; border-style: solid; @@ -195,6 +208,7 @@ span.ag-math > .ag-math-render.ag-math-error { content: ""; } +.ag-hide.ag-ruby > .ag-ruby-render::before .ag-hide.ag-math > .ag-math-render::before { content: none; } @@ -209,7 +223,7 @@ figure { width: 100%; user-select: none; position: absolute; - top: -15px; + top: -20px; left: 0; display: none; } @@ -720,3 +734,7 @@ span.ag-reference-link { .ag-meta-or-ctrl a.ag-inline-rule { cursor: pointer !important; } + +.ag-ruby-render { + user-select: none; +} diff --git a/src/muya/lib/config/index.js b/src/muya/lib/config/index.js index 616ae917..5721894d 100644 --- a/src/muya/lib/config/index.js +++ b/src/muya/lib/config/index.js @@ -109,6 +109,9 @@ export const CLASS_OR_ID = genUpper2LowerKeyHash([ 'AG_MATH', 'AG_MATH_TEXT', 'AG_MATH_RENDER', + 'AG_RUBY', + 'AG_RUBY_TEXT', + 'AG_RUBY_RENDER', 'AG_MATH_ERROR', 'AG_EMPTY', 'AG_MATH_MARKER', @@ -238,3 +241,10 @@ export const isInElectron = window && window.process && window.process.type === export const isOsx = window && window.navigator && /Mac/.test(window.navigator.platform) // http[s] (domain or IPv4 or localhost or IPv6) [port] /not-white-space export const URL_REG = /^http(s)?:\/\/([a-z0-9\-._~]+\.[a-z]{2,}|[0-9.]+|localhost|\[[a-f0-9.:]+\])(:[0-9]{1,5})?\/[\S]+/i + +// selected from https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes +export const WHITELIST_ATTRIBUTES = [ + 'align', 'alt', 'checked', 'class', 'color', 'dir', 'disabled', 'for', 'height', 'hidden', + 'href', 'id', 'lang', 'lazyload', 'rel', 'spellcheck', 'src', 'srcset', 'start', 'style', + 'target', 'title', 'type', 'value', 'width' +] diff --git a/src/muya/lib/contentState/copyCutCtrl.js b/src/muya/lib/contentState/copyCutCtrl.js index dbd536e1..8e3f1316 100644 --- a/src/muya/lib/contentState/copyCutCtrl.js +++ b/src/muya/lib/contentState/copyCutCtrl.js @@ -26,6 +26,7 @@ const copyCutCtrl = ContentState => { const removedElements = wrapper.querySelectorAll( `.${CLASS_OR_ID['AG_TOOL_BAR']}, .${CLASS_OR_ID['AG_MATH_RENDER']}, + .${CLASS_OR_ID['AG_RUBY_RENDER']}, .${CLASS_OR_ID['AG_HTML_PREVIEW']}, .${CLASS_OR_ID['AG_MATH_PREVIEW']}, .${CLASS_OR_ID['AG_COPY_REMOVE']}, diff --git a/src/muya/lib/contentState/inputCtrl.js b/src/muya/lib/contentState/inputCtrl.js index 3e36a9a9..57f70a50 100644 --- a/src/muya/lib/contentState/inputCtrl.js +++ b/src/muya/lib/contentState/inputCtrl.js @@ -50,7 +50,7 @@ const inputCtrl = ContentState => { const key = start.key const block = this.getBlock(key) const paragraph = document.querySelector(`#${key}`) - let text = getTextContent(paragraph, [ CLASS_OR_ID['AG_MATH_RENDER'] ]) + let text = getTextContent(paragraph, [ CLASS_OR_ID['AG_MATH_RENDER'], CLASS_OR_ID['AG_RUBY_RENDER'] ]) let needRender = false let needRenderAll = false diff --git a/src/muya/lib/contentState/tabCtrl.js b/src/muya/lib/contentState/tabCtrl.js index dbc0381e..0720ba9e 100644 --- a/src/muya/lib/contentState/tabCtrl.js +++ b/src/muya/lib/contentState/tabCtrl.js @@ -198,8 +198,7 @@ const tabCtrl = ContentState => { start.key === end.key && start.offset === end.offset && startBlock.type === 'span' && - startBlock.functionType === 'codeLine' && - startBlock.lang === 'markup' + (!startBlock.functionType || startBlock.functionType === 'codeLine' && startBlock.lang === 'markup') ) { const { text } = startBlock const lastWord = text.split(/\s+/).pop() diff --git a/src/muya/lib/eventHandler/clickEvent.js b/src/muya/lib/eventHandler/clickEvent.js index 0bc546f7..bc582128 100644 --- a/src/muya/lib/eventHandler/clickEvent.js +++ b/src/muya/lib/eventHandler/clickEvent.js @@ -59,7 +59,9 @@ class ClickEvent { // handler image and inline math preview click const markedImageText = target.previousElementSibling const mathRender = target.closest(`.${CLASS_OR_ID['AG_MATH_RENDER']}`) + const rubyRender = target.closest(`.${CLASS_OR_ID['AG_RUBY_RENDER']}`) const mathText = mathRender && mathRender.previousElementSibling + const rubyText = rubyRender && rubyRender.previousElementSibling if (markedImageText && markedImageText.classList.contains(CLASS_OR_ID['AG_IMAGE_MARKED_TEXT'])) { eventCenter.dispatch('format-click', { event, @@ -69,6 +71,8 @@ class ClickEvent { selectionText(markedImageText) } else if (mathText) { selectionText(mathText) + } else if (rubyText) { + selectionText(rubyText) } // handler html preview click const htmlPreview = target.closest(`.ag-function-html`) diff --git a/src/muya/lib/parser/parse.js b/src/muya/lib/parser/parse.js index 1d9e9f27..5516612d 100644 --- a/src/muya/lib/parser/parse.js +++ b/src/muya/lib/parser/parse.js @@ -1,9 +1,10 @@ import { beginRules, inlineRules } from './rules' import { isLengthEven, union } from '../utils' -import { punctuation } from '../config' +import { punctuation, WHITELIST_ATTRIBUTES } from '../config' const CAN_NEST_RULES = ['strong', 'em', 'link', 'del', 'image', 'a_link'] // image can not nest but it has children - +// disallowed html tags in https://github.github.com/gfm/#raw-html +const disallowedHtmlTag = /(?:title|textarea|style|xmp|iframe|noembed|noframes|script|plaintext)/i const validateRules = Object.assign({}, inlineRules) delete validateRules.em delete validateRules.strong @@ -16,21 +17,22 @@ const validWidthAndHeight = value => { return value >= 0 ? value : '' } -const getSrcAlt = text => { - const SRC_REG = /src\s*=\s*("|')([^\1]+?)\1/ - const ALT_REG = /alt\s*=\s*("|')([^\1]+?)\1/ - const WIDTH_REG = /width\s*=\s*("|')([^\1]+?)\1/ - const HEIGHT_REG = /height\s*=\s*("|')([^\1]+?)\1/ - const srcMatch = SRC_REG.exec(text) - const src = srcMatch ? srcMatch[2] : '' - const altMatch = ALT_REG.exec(text) - const alt = altMatch ? altMatch[2] : '' - const widthMatch = WIDTH_REG.exec(text) - const width = widthMatch ? validWidthAndHeight(widthMatch[2]) : '' - const heightMatch = HEIGHT_REG.exec(text) - const height = heightMatch ? validWidthAndHeight(heightMatch[2]) : '' +const getAttributes = html => { + const parser = new DOMParser() + const doc = parser.parseFromString(html, 'text/html') + const target = doc.querySelector('body').firstElementChild + if (!target) return null + const attrs = {} + for (const attr of target.getAttributeNames()) { + if (!WHITELIST_ATTRIBUTES.includes(attr)) continue + if (/width|height/.test(attr)) { + attrs[attr] = validWidthAndHeight(target.getAttribute(attr)) + } else { + attrs[attr] = target.getAttribute(attr) + } + } - return { src, alt, width, height } + return attrs } const lowerPriority = (src, offset) => { @@ -364,57 +366,6 @@ const tokenizerFac = (src, beginRules, inlineRules, pos = 0, top) => { continue } - // a_link `Anchor` - const aLinkTo = inlineRules['a_link'].exec(src) - if (aLinkTo) { - pushPending() - tokens.push({ - type: 'a_link', - raw: aLinkTo[0], - href: aLinkTo[3], - openTag: aLinkTo[1], - closeTag: aLinkTo[5], - anchor: aLinkTo[4], - parent: tokens, - range: { - start: pos, - end: pos + aLinkTo[0].length - }, - children: tokenizerFac(aLinkTo[4], undefined, inlineRules, pos + aLinkTo[1].length, false) - }) - - src = src.substring(aLinkTo[0].length) - pos = pos + aLinkTo[0].length - continue - } - - // html-image - const htmlImageTo = inlineRules['html_image'].exec(src) - if (htmlImageTo) { - const rawAttr = htmlImageTo[2] - const { src: imageSrc, alt, width, height } = getSrcAlt(rawAttr) - if (imageSrc) { - pushPending() - tokens.push({ - type: 'html_image', - raw: htmlImageTo[0], - tag: htmlImageTo[1], - parent: tokens, - src: imageSrc, - width, - height, - alt, - range: { - start: pos, - end: pos + htmlImageTo[0].length - } - }) - src = src.substring(htmlImageTo[0].length) - pos = pos + htmlImageTo[0].length - continue - } - } - // html escape const htmlEscapeTo = inlineRules['html_escape'].exec(src) if (htmlEscapeTo) { @@ -437,14 +388,43 @@ const tokenizerFac = (src, beginRules, inlineRules, pos = 0, top) => { // html-tag const htmlTo = inlineRules['html_tag'].exec(src) - if (htmlTo) { + let attrs + // handle comment + if (htmlTo && htmlTo[1] && !htmlTo[3]) { const len = htmlTo[0].length pushPending() tokens.push({ type: 'html_tag', raw: htmlTo[0], - tag: htmlTo[1], + tag: '', + openTag: htmlTo[1], parent: tokens, + attrs: {}, + range: { + start: pos, + end: pos + len + } + }) + src = src.substring(len) + pos = pos + len + continue + } + if (htmlTo && !(disallowedHtmlTag.test(htmlTo[3])) && (attrs = getAttributes(htmlTo[0]))) { + const tag = htmlTo[3] + const html = htmlTo[0] + const len = htmlTo[0].length + + pushPending() + tokens.push({ + type: 'html_tag', + raw: html, + tag, + openTag: htmlTo[2], + closeTag: htmlTo[5], + parent: tokens, + attrs, + content: htmlTo[4], + children: htmlTo[4] ? tokenizerFac(htmlTo[4], undefined, inlineRules, pos + htmlTo[2].length, false) : '', range: { start: pos, end: pos + len diff --git a/src/muya/lib/parser/render/renderBlock/renderLeafBlock.js b/src/muya/lib/parser/render/renderBlock/renderLeafBlock.js index c52103c7..c97f59ba 100644 --- a/src/muya/lib/parser/render/renderBlock/renderLeafBlock.js +++ b/src/muya/lib/parser/render/renderBlock/renderLeafBlock.js @@ -84,6 +84,7 @@ export default function renderLeafBlock (block, cursor, activeBlocks, matches, u this.tokenCache.set(text, tokens) } } + children = tokens.reduce((acc, token) => [...acc, ...this[snakeToCamel(token.type)](h, cursor, block, token)], []) } diff --git a/src/muya/lib/parser/render/renderInlines/aLink.js b/src/muya/lib/parser/render/renderInlines/aLink.js deleted file mode 100644 index 4faffc63..00000000 --- a/src/muya/lib/parser/render/renderInlines/aLink.js +++ /dev/null @@ -1,25 +0,0 @@ -import { CLASS_OR_ID } from '../../../config' -import { snakeToCamel } from '../../../utils' - -// `a_link`: `anchor` -export default function aLink (h, cursor, block, token, outerClass) { - const className = this.getClassName(outerClass, block, token, cursor) - const tagClassName = className === CLASS_OR_ID['AG_HIDE'] ? className : CLASS_OR_ID['AG_HTML_TAG'] - const { start, end } = token.range - const openTag = this.highlight(h, block, start, start + token.openTag.length, token) - const anchor = token.children.reduce((acc, to) => { - const chunk = this[snakeToCamel(to.type)](h, cursor, block, to, className) - return Array.isArray(chunk) ? [...acc, ...chunk] : [...acc, chunk] - }, []) - const closeTag = this.highlight(h, block, end - token.closeTag.length, end, token) - - return [ - h(`span.${tagClassName}.${CLASS_OR_ID['AG_OUTPUT_REMOVE']}`, openTag), - h(`a.${CLASS_OR_ID['AG_A_LINK']}`, { - dataset: { - href: token.href - } - }, anchor), - h(`span.${tagClassName}.${CLASS_OR_ID['AG_OUTPUT_REMOVE']}`, closeTag) - ] -} diff --git a/src/muya/lib/parser/render/renderInlines/htmlImage.js b/src/muya/lib/parser/render/renderInlines/htmlImage.js index d6ecf4c1..db1535d3 100644 --- a/src/muya/lib/parser/render/renderInlines/htmlImage.js +++ b/src/muya/lib/parser/render/renderInlines/htmlImage.js @@ -7,7 +7,7 @@ export default function htmlImage (h, cursor, block, token, outerClass) { const imageClass = CLASS_OR_ID['AG_IMAGE_MARKED_TEXT'] const { start, end } = token.range const tag = this.highlight(h, block, start, end, token) - const { src: rawSrc, alt, width, height } = token + const { src: rawSrc, alt = '', width, height } = token.attrs const imageInfo = getImageInfo(rawSrc) const { src } = imageInfo let id diff --git a/src/muya/lib/parser/render/renderInlines/htmlRuby.js b/src/muya/lib/parser/render/renderInlines/htmlRuby.js new file mode 100644 index 00000000..54a48ebd --- /dev/null +++ b/src/muya/lib/parser/render/renderInlines/htmlRuby.js @@ -0,0 +1,27 @@ +import { CLASS_OR_ID } from '../../../config' +import { htmlToVNode } from '../snabbdom' + +export default function htmlRuby (h, cursor, block, token, outerClass) { + const className = this.getClassName(outerClass, block, token, cursor) + const { children } = token + const { start, end } = token.range + const content = this.highlight(h, block, start, end, token) + const vNode = htmlToVNode(token.raw) + + const previewSelector = `span.${CLASS_OR_ID['AG_RUBY_RENDER']}` + + + return children ? [ + h(`span.${className}.${CLASS_OR_ID['AG_RUBY']}`, [ + h(`span.${CLASS_OR_ID['AG_INLINE_RULE']}.${CLASS_OR_ID['AG_RUBY_TEXT']}`, content), + h(previewSelector, { + attrs: { contenteditable: 'false' } + }, vNode) + ]) + // if children is empty string, no need to render ruby charactors... + ] : [ + h(`span.${className}.${CLASS_OR_ID['AG_RUBY']}`, [ + h(`span.${CLASS_OR_ID['AG_INLINE_RULE']}.${CLASS_OR_ID['AG_RUBY_TEXT']}`, content) + ]) + ] +} diff --git a/src/muya/lib/parser/render/renderInlines/htmlTag.js b/src/muya/lib/parser/render/renderInlines/htmlTag.js index 8b0b736d..8a7ee10e 100644 --- a/src/muya/lib/parser/render/renderInlines/htmlTag.js +++ b/src/muya/lib/parser/render/renderInlines/htmlTag.js @@ -1,11 +1,66 @@ -import { CLASS_OR_ID } from '../../../config' +import { CLASS_OR_ID, BLOCK_TYPE6 } from '../../../config' +import { snakeToCamel } from '../../../utils' export default function htmlTag (h, cursor, block, token, outerClass) { - const className = CLASS_OR_ID['AG_HTML_TAG'] + const { tag, openTag, closeTag, children, attrs } = token + const className = children ? this.getClassName(outerClass, block, token, cursor) : CLASS_OR_ID['AG_GRAY'] + const tagClassName = className === CLASS_OR_ID['AG_HIDE'] ? className : CLASS_OR_ID['AG_HTML_TAG'] const { start, end } = token.range - const tag = this.highlight(h, block, start, end, token) - const isBr = /)/.test(token.tag) - return [ - h(`span.${className}`, isBr ? [...tag, h('br')] : tag) - ] + const openContent = this.highlight(h, block, start, start + openTag.length, token) + const closeContent = closeTag + ? this.highlight(h, block, end - closeTag.length, end, token) + : '' + + const anchor = Array.isArray(children) && tag !== 'ruby' // important + ? children.reduce((acc, to) => { + const chunk = this[snakeToCamel(to.type)](h, cursor, block, to, className) + return Array.isArray(chunk) ? [...acc, ...chunk] : [...acc, chunk] + }, []) + : '' + switch (tag) { + case 'img': { + return this.htmlImage(h, cursor, block, token, outerClass) + } + case 'br': { + return [h(`span.${CLASS_OR_ID['AG_HTML_TAG']}`, [...openContent, h(tag)])] + } + default: + // handle void html tag + if (!closeTag) { + return [h(`span.${CLASS_OR_ID['AG_HTML_TAG']}`, openContent)] + } else if (tag === 'ruby') { + return this.htmlRuby(h, cursor, block, token, outerClass) + } else { + // if tag is a block level element, use a inline element `span` to instead. + // Because we can not nest a block level element in span element(line is span element) + // we also recommand user not use block level element in paragraph. use block element in html block. + let selector = BLOCK_TYPE6.includes(tag) ? 'span' : tag + selector += `.${CLASS_OR_ID['AG_INLINE_RULE']}` + const data = { + attrs: {}, + dataset: {} + } + if (attrs.id) { + selector += `#${attrs.id}` + } + if (attrs.class && /\S/.test(attrs.class)) { + const classNames = attrs.class.split(/\s+/) + for (const className of classNames) { + selector += `.${className}` + } + } + + for (const attr of Object.keys(attrs)) { + if (attr !== 'id' && attr !== 'class') { + data.attrs[attr] = attrs[attr] + } + } + + return [ + h(`span.${tagClassName}.${CLASS_OR_ID['AG_OUTPUT_REMOVE']}`, openContent), + h(`${selector}`, data, anchor), + h(`span.${tagClassName}.${CLASS_OR_ID['AG_OUTPUT_REMOVE']}`, closeContent) + ] + } + } } diff --git a/src/muya/lib/parser/render/renderInlines/index.js b/src/muya/lib/parser/render/renderInlines/index.js index b454cc7c..4c5053f8 100644 --- a/src/muya/lib/parser/render/renderInlines/index.js +++ b/src/muya/lib/parser/render/renderInlines/index.js @@ -9,7 +9,6 @@ import tailHeader from './tailHeader' import hardLineBreak from './hardLineBreak' import codeFense from './codeFense' import inlineMath from './inlineMath' -import aLink from './aLink' import autoLink from './autoLink' import loadImageAsync from './loadImageAsync' import htmlImage from './htmlImage' @@ -24,6 +23,7 @@ import strong from './strong' import htmlEscape from './htmlEscape' import multipleMath from './multipleMath' import referenceDefinition from './referenceDefinition' +import htmlRuby from './htmlRuby' import referenceLink from './referenceLink' import referenceImage from './referenceImage' @@ -39,7 +39,6 @@ export default { hardLineBreak, codeFense, inlineMath, - aLink, autoLink, loadImageAsync, htmlImage, @@ -54,6 +53,7 @@ export default { htmlEscape, multipleMath, referenceDefinition, + htmlRuby, referenceLink, referenceImage } diff --git a/src/muya/lib/parser/rules.js b/src/muya/lib/parser/rules.js index 7df135db..5f75a9bb 100644 --- a/src/muya/lib/parser/rules.js +++ b/src/muya/lib/parser/rules.js @@ -23,9 +23,7 @@ export const inlineRules = { 'reference_link': /^\[([^\]]+?)(\\*)\](?:\[([^\]]*?)(\\*)\])?/, 'reference_image': /^\!\[([^\]]+?)(\\*)\](?:\[([^\]]*?)(\\*)\])?/, 'tail_header': /^(\s{1,}#{1,})(\s*)$/, - 'a_link': /^()[\s\S]*(?!\\)>)([\s\S]*)(<\/a>)/, // can nest - 'html_image': /^()/, - 'html_tag': /^(|<\/?[a-zA-Z\d-]+[\s\S]*?(?!\\)>)/, + 'html_tag': /^(|(<([a-zA-Z]{1}[a-zA-Z\d-]*) *[_\.\-/:a-zA-Z\d='"; ]* *(?:\/)?>)(?:([\s\S]*?)(<\/\3 *>))?)/, // row html 'html_escape': new RegExp(`^(${escapeCharacters.join('|')})`, 'i'), 'hard_line_break': /^(\s{2,})$/, diff --git a/src/muya/lib/selection/index.js b/src/muya/lib/selection/index.js index 96b1c194..a9a24c1e 100644 --- a/src/muya/lib/selection/index.js +++ b/src/muya/lib/selection/index.js @@ -420,10 +420,10 @@ class Selection { let count = 0 for (i = 0; i < len; i++) { const child = childNodes[i] - if (count + getTextContent(child, [ CLASS_OR_ID['AG_MATH_RENDER'] ]).length >= offset) { + if (count + getTextContent(child, [ CLASS_OR_ID['AG_MATH_RENDER'], CLASS_OR_ID['AG_RUBY_RENDER'] ]).length >= offset) { return getNodeAndOffset(child, offset - count) } else { - count += getTextContent(child, [ CLASS_OR_ID['AG_MATH_RENDER'] ]).length + count += getTextContent(child, [ CLASS_OR_ID['AG_MATH_RENDER'], CLASS_OR_ID['AG_RUBY_RENDER'] ]).length } } return { node, offset } @@ -471,7 +471,7 @@ class Selection { do { preSibling = preSibling.previousSibling if (preSibling) { - offset += getTextContent(preSibling, [ CLASS_OR_ID['AG_MATH_RENDER'] ]).length + offset += getTextContent(preSibling, [ CLASS_OR_ID['AG_MATH_RENDER'], CLASS_OR_ID['AG_RUBY_RENDER'] ]).length } } while (preSibling) return (node === paragraph || node.parentNode === paragraph) diff --git a/src/muya/lib/ui/codePicker/index.css b/src/muya/lib/ui/codePicker/index.css index 9e044235..d1b13e6b 100644 --- a/src/muya/lib/ui/codePicker/index.css +++ b/src/muya/lib/ui/codePicker/index.css @@ -26,7 +26,8 @@ color: var(--editorColor); } -.ag-list-picker .active, .ag-list-picker .item:hover { +.ag-list-picker .item:hover, +.ag-list-picker .item.active { background-color: var(--floatHoverColor); } diff --git a/src/muya/themes/default.css b/src/muya/themes/default.css index 9ee96865..7b3123ba 100644 --- a/src/muya/themes/default.css +++ b/src/muya/themes/default.css @@ -112,6 +112,17 @@ pre.ag-paragraph { font-size: 14px; } +kbd { + color: var(--editorColor); + background: var(--floatBgColor); + border: 1px solid var(--floatBorderColor); + border-radius: 4px; + display: inline-block; + transform: scale(.8); + padding: 0px 5px; + box-shadow: inset 0 -1px 0 var(--floatBorderColor); +} + @media not print { #ag-editor-id { @@ -414,19 +425,22 @@ pre[class*="language-"] { color: var(--editorColor50); } +.ag-hide.ag-ruby > .ag-ruby-render, .ag-hide.ag-math > .ag-math-render { color: var(--editorColor); } - +blockquote .ag-hide.ag-ruby > .ag-ruby-render, blockquote .ag-hide.ag-math > .ag-math-render { color: var(--editorColor50); } +.ag-gray.ag-ruby > .ag-ruby-render, .ag-gray.ag-math > .ag-math-render { color: var(--editorColor); background: var(--floatBgColor); box-shadow: 0 4px 8px 0 var(--floatBorderColor); } +.ag-gray.ag-ruby > .ag-ruby-render::before, .ag-gray.ag-math > .ag-math-render::before { border-bottom-color: var(--floatBgColor); } @@ -458,7 +472,7 @@ body.dark .ag-image-marked-text.ag-image-fail::before { } .ag-front-icon { - fill: var(--editorColor50); + fill: var(--editorColor30); } .ag-front-icon::before {