diff --git a/app/src/assets/scss/_typography.scss b/app/src/assets/scss/_typography.scss index c19656d8f..5cccb4d40 100644 --- a/app/src/assets/scss/_typography.scss +++ b/app/src/assets/scss/_typography.scss @@ -97,6 +97,28 @@ } } + kbd, + span[data-type~="kbd"] { + padding: 2px 4px; + font: 75% Consolas, "Liberation Mono", Menlo, Courier, monospace; + line-height: 1; + color: var(--b3-theme-on-surface); + vertical-align: middle; + background-color: var(--b3-theme-surface); + border: solid 1px var(--b3-border-color); + border-radius: 3px; + box-shadow: inset 0 -1px 0 var(--b3-border-color); + } + + u { + text-decoration: none; + } + + u, + span[data-type~="u"] { + border-bottom: 1px solid var(--b3-theme-on-background); + } + span[data-type~="inline-memo"] { background-color: var(--b3-card-info-background); border-bottom: 2px solid var(--b3-card-info-color); @@ -180,28 +202,6 @@ } } - kbd, - span[data-type~="kbd"] { - padding: 2px 4px; - font: 75% Consolas, "Liberation Mono", Menlo, Courier, monospace; - line-height: 1; - color: var(--b3-theme-on-surface); - vertical-align: middle; - background-color: var(--b3-theme-surface); - border: solid 1px var(--b3-border-color); - border-radius: 3px; - box-shadow: inset 0 -1px 0 var(--b3-border-color); - } - - u { - text-decoration: none; - } - - u, - span[data-type~="u"] { - border-bottom: 1px solid var(--b3-theme-on-background); - } - table { border-collapse: collapse; empty-cells: show; diff --git a/app/src/protyle/toolbar/InlineMemo.ts b/app/src/protyle/toolbar/InlineMemo.ts index 9aac1511d..7c2b92911 100644 --- a/app/src/protyle/toolbar/InlineMemo.ts +++ b/app/src/protyle/toolbar/InlineMemo.ts @@ -4,6 +4,7 @@ import {updateTransaction} from "../wysiwyg/transaction"; import {hasClosestBlock, hasClosestByAttribute} from "../util/hasClosest"; import {hasNextSibling, hasPreviousSibling} from "../wysiwyg/getBlock"; import {fixTableRange} from "../util/selection"; +import {isArrayEqual} from "../../util/functions"; export class InlineMemo extends ToolbarItem { public element: HTMLElement; @@ -41,12 +42,47 @@ export class InlineMemo extends ToolbarItem { range.insertNode(wbrElement); const html = nodeElement.outerHTML; - const newElement = document.createElement("span"); - newElement.innerHTML = range.toString(); - newElement.setAttribute("data-type", "inline-memo"); - range.extractContents(); - range.insertNode(newElement); - protyle.toolbar.showRender(protyle, newElement); + const newNodes: Element[] = []; + const contents = range.extractContents(); + contents.childNodes.forEach((item: HTMLElement) => { + if (item.nodeType === 3) { + const inlineElement = document.createElement("span"); + inlineElement.setAttribute("data-type", "inline-memo"); + inlineElement.textContent = item.textContent; + newNodes.push(inlineElement); + } else { + let types = (item.getAttribute("data-type") || "").split(" "); + types.push("inline-memo"); + types = [...new Set(types)]; + if (item.tagName !== "BR" && item.tagName !== "WBR") { + item.setAttribute("data-type", types.join(" ")); + newNodes.push(item); + } else if (item.tagName !== "WBR") { + newNodes.push(item); + } + } + }); + for (let i = 0; i < newNodes.length; i++) { + const currentNewNode = newNodes[i] as HTMLElement; + const nextNewNode = newNodes[i + 1] as HTMLElement; + if (currentNewNode.nodeType !== 3 && nextNewNode && nextNewNode.nodeType !== 3 && + isArrayEqual(nextNewNode.getAttribute("data-type").split(" "), currentNewNode.getAttribute("data-type").split(" ")) && + currentNewNode.style.color === nextNewNode.style.color && + currentNewNode.style.webkitTextFillColor === nextNewNode.style.webkitTextFillColor && + currentNewNode.style.webkitTextStroke === nextNewNode.style.webkitTextStroke && + currentNewNode.style.textShadow === nextNewNode.style.textShadow && + currentNewNode.style.backgroundColor === nextNewNode.style.backgroundColor) { + // 合并相同的 node + nextNewNode.innerHTML = currentNewNode.innerHTML + nextNewNode.innerHTML; + newNodes.splice(i, 1); + i--; + } else { + range.insertNode(newNodes[i]); + range.collapse(false); + } + } + range.setStart(newNodes[0].firstChild, 0); + protyle.toolbar.showRender(protyle, newNodes[0], newNodes); nodeElement.setAttribute("updated", dayjs().format("YYYYMMDDHHmmss")); updateTransaction(protyle, nodeElement.getAttribute("data-node-id"), nodeElement.outerHTML, html); wbrElement.remove(); diff --git a/app/src/protyle/toolbar/index.ts b/app/src/protyle/toolbar/index.ts index 29099f5dc..5452b3a44 100644 --- a/app/src/protyle/toolbar/index.ts +++ b/app/src/protyle/toolbar/index.ts @@ -599,7 +599,7 @@ export class Toolbar { anchorElement.select(); } - public showRender(protyle: IProtyle, renderElement: Element) { + public showRender(protyle: IProtyle, renderElement: Element, updateElements?: Element[]) { const nodeElement = hasClosestBlock(renderElement); if (!nodeElement) { return; @@ -859,18 +859,33 @@ export class Toolbar { } } if (isInlineMemo) { - if (!target.value) { - // https://github.com/siyuan-note/insider/issues/1046 - if (type.replace("inline-memo", "") === "") { - renderElement.outerHTML = renderElement.innerHTML + ""; - focusByWbr(nodeElement, this.range); - } else { - renderElement.setAttribute("data-type", type.replace("inline-memo", "")); - renderElement.removeAttribute("data-inline-memo-content"); - } + let inlineMemoElements + if (updateElements) { + inlineMemoElements = updateElements } else { - renderElement.setAttribute("data-inline-memo-content", Lute.EscapeHTMLStr(target.value)); + inlineMemoElements = [renderElement] } + inlineMemoElements.forEach((item, index) => { + if (!target.value) { + // https://github.com/siyuan-note/insider/issues/1046 + const currentTypes = item.getAttribute("data-type").split(" ") + if (currentTypes.length === 1 && currentTypes[0] === "inline-memo") { + item.outerHTML = item.innerHTML + (index === inlineMemoElements.length - 1 ? "" : ""); + focusByWbr(nodeElement, this.range); + } else { + currentTypes.find((typeItem, index) => { + if (typeItem === "inline-memo") { + currentTypes.splice(index, 1) + return true + } + }) + item.setAttribute("data-type", currentTypes.join(" ")); + item.removeAttribute("data-inline-memo-content"); + } + } else { + item.setAttribute("data-inline-memo-content", Lute.EscapeHTMLStr(target.value)); + } + }) } else if (type === "NodeBlockQueryEmbed") { blockRender(protyle, renderElement); } @@ -893,6 +908,9 @@ export class Toolbar { } updateTransaction(protyle, id, newHTML, html); html = newHTML; + if (isInlineMemo && !target.value) { + this.subElement.classList.add("fn__none"); + } event.stopPropagation(); }); textElement.addEventListener("keydown", (event: KeyboardEvent) => {