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
+foozar
+```
+
+*Recommand*
+
+```md
+
+ foo
+
+ 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 {