diff --git a/app/src/assets/scss/_typography.scss b/app/src/assets/scss/_typography.scss index 6a635cdad..663f31b1b 100644 --- a/app/src/assets/scss/_typography.scss +++ b/app/src/assets/scss/_typography.scss @@ -52,7 +52,8 @@ color: var(--b3-protyle-inline-s-color); } - strong { + strong, + span[data-type~="strong"] { color: var(--b3-protyle-inline-strong-color); } diff --git a/app/src/assets/scss/_wysiwyg.scss b/app/src/assets/scss/_wysiwyg.scss index b3af986f9..cffdf4190 100644 --- a/app/src/assets/scss/_wysiwyg.scss +++ b/app/src/assets/scss/_wysiwyg.scss @@ -169,6 +169,10 @@ } } + span[data-type~="strong"] { + font-weight: bold; + } + span[data-type="tag"] { border-bottom: 1px solid var(--b3-protyle-inline-tag-color); color: var(--b3-protyle-inline-tag-color); diff --git a/app/src/constants.ts b/app/src/constants.ts index 603b6bd4e..c15fe5a37 100644 --- a/app/src/constants.ts +++ b/app/src/constants.ts @@ -397,7 +397,7 @@ export abstract class Constants { "lightfair", "magula", "mono-blue", "nnfx-light", "paraiso-light", "purebasic", "qtcreator-light", "routeros", "school-book", "stackoverflow-light", "tokyo-night-light", "vs", "xcode", "default"]; public static readonly ZWSP: string = "\u200b"; - public static readonly INLINE_TYPE: string[] = ["link", "bold", "italic", "underline", "strike", "mark", "sup", "sub", "tag", "inline-code", "inline-math"]; + public static readonly INLINE_TYPE: string[] = ["block-ref", "kbd", "text", "file-annotation-ref", "a", "strong", "em", "u", "s", "mark", "sup", "sub", "tag", "code", "inline-math"]; public static readonly BLOCK_HINT_KEYS: string[] = ["((", "[[", "((", "【【"]; public static readonly BLOCK_HINT_CLOSE_KEYS: IObject = {"((": "))", "[[": "]]", "((": "))", "【【": "】】"}; public static readonly CODE_LANGUAGES: string[] = [ diff --git a/app/src/protyle/hint/extend.ts b/app/src/protyle/hint/extend.ts index 976d7d5ac..98ee428be 100644 --- a/app/src/protyle/hint/extend.ts +++ b/app/src/protyle/hint/extend.ts @@ -110,23 +110,23 @@ export const hintSlash = (key: string, protyle: IProtyle) => { html: `
${window.siyuan.languages.emoji}:
`, }, { filter: ["链接", "lianjie", "lj", "link"], - value: "link", + value: "a", html: `
${window.siyuan.languages.link}${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.link.custom))}
`, }, { - filter: ["粗体", "cuti", "ct", "bold"], - value: "bold", + filter: ["粗体", "cuti", "ct", "bold", "strong"], + value: "strong", html: `
${window.siyuan.languages.bold}${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.bold.custom))}
`, }, { - filter: ["斜体", "xieti", "xt", "italic"], - value: "italic", + filter: ["斜体", "xieti", "xt", "italic", "em"], + value: "em", html: `
${window.siyuan.languages.italic}${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.italic.custom))}
`, }, { filter: ["下划线", "xiahuaxian", "xhx", "underline"], - value: "underline", + value: "u", html: `
${window.siyuan.languages.underline}${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.underline.custom))}
`, }, { filter: ["删除线", "shanchuxian", "scx", "strike"], - value: "strike", + value: "s", html: `
${window.siyuan.languages.strike}${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.strike.custom))}
`, }, { filter: ["标记", "biaoji", "bj", "mark"], @@ -146,7 +146,7 @@ export const hintSlash = (key: string, protyle: IProtyle) => { html: `
${window.siyuan.languages.tag}${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.tag.custom))}
`, }, { filter: ["行内代码", "hangneidaima", "hndm", "inline code"], - value: "inline-code", + value: "code", html: `
${window.siyuan.languages["inline-code"]}${updateHotkeyTip((window.siyuan.config.keymap.editor.insert["inline-code"].custom))}
`, }, { filter: ["行内数学公式", "hangneishuxuegongshi", "hnsxgs", "inline math"], diff --git a/app/src/protyle/toolbar/Font.ts b/app/src/protyle/toolbar/Font.ts index 19f420008..bb02c0a50 100644 --- a/app/src/protyle/toolbar/Font.ts +++ b/app/src/protyle/toolbar/Font.ts @@ -120,7 +120,7 @@ export const fontEvent = (protyle: IProtyle, type?: string, color?: string) => { color = fontStyles[1]; } } - protyle.toolbar.setInlineMark(protyle, "bold", "add", true); + protyle.toolbar.setInlineMark(protyle, "text", "add", true); const range = protyle.toolbar.range; const fontElement = hasClosestByMatchTag(range.startContainer, "STRONG"); if (!fontElement) { diff --git a/app/src/protyle/toolbar/ToolbarItem.ts b/app/src/protyle/toolbar/ToolbarItem.ts index 30859636c..de9bc3a68 100644 --- a/app/src/protyle/toolbar/ToolbarItem.ts +++ b/app/src/protyle/toolbar/ToolbarItem.ts @@ -7,7 +7,7 @@ export class ToolbarItem { constructor(protyle: IProtyle, menuItem: IMenuItem) { this.element = document.createElement("button"); const hotkey = menuItem.hotkey ? ` ${updateHotkeyTip(menuItem.hotkey)}` : ""; - const tip = menuItem.tip || window.siyuan.languages[menuItem.name]; + const tip = menuItem.tip || window.siyuan.languages[menuItem.lang]; this.element.classList.add("protyle-toolbar__item", "b3-tooltips", `b3-tooltips__${menuItem.tipPosition}`); this.element.setAttribute("data-type", menuItem.name); this.element.setAttribute("aria-label", tip + hotkey); diff --git a/app/src/protyle/toolbar/index.ts b/app/src/protyle/toolbar/index.ts index f25baa908..24985b89c 100644 --- a/app/src/protyle/toolbar/index.ts +++ b/app/src/protyle/toolbar/index.ts @@ -134,31 +134,31 @@ export class Toolbar { } public getCurrentType(range = this.range) { - const types: string[] = []; + let types: string[] = []; let startElement = range.startContainer as HTMLElement; if (startElement.nodeType === 3) { startElement = startElement.parentElement; - if (startElement.getAttribute("data-type") === "virtual-block-ref" && !["DIV", "TD", "TH"].includes(startElement.parentElement.tagName)) { - startElement = startElement.parentElement; - } } else if (startElement.childElementCount > 0 && startElement.childNodes[range.startOffset]?.nodeType !== 3) { startElement = startElement.childNodes[range.startOffset] as HTMLElement; } if (!startElement || startElement.nodeType === 3) { return []; } + if (!["DIV", "TD", "TH"].includes(startElement.tagName)) { + types = startElement.getAttribute("data-type").split(" "); + } let endElement = range.endContainer as HTMLElement; if (endElement.nodeType === 3) { endElement = endElement.parentElement; - if (endElement.getAttribute("data-type") === "virtual-block-ref" && !["DIV", "TD", "TH"].includes(endElement.parentElement.tagName)) { - endElement = endElement.parentElement; - } } else if (endElement.childElementCount > 0 && endElement.childNodes[range.endOffset]?.nodeType !== 3) { endElement = endElement.childNodes[range.endOffset] as HTMLElement; } if (!endElement || endElement.nodeType === 3) { return []; } + if (!["DIV", "TD", "TH"].includes(endElement.tagName)) { + types = types.concat(endElement.getAttribute("data-type").split(" ")); + } if (range.startOffset === range.startContainer.textContent.length) { const nextSibling = hasNextSibling(range.startContainer as Element); if (nextSibling && nextSibling.nodeType !== 3 && (nextSibling as Element).getAttribute("data-type") === "inline-math") { @@ -170,77 +170,41 @@ export class Toolbar { types.push("inline-math"); } } - if (startElement.tagName === "STRONG" || endElement.tagName === "STRONG") { - types.push("bold"); - } - if (startElement.tagName === "EM" || endElement.tagName === "EM") { - types.push("italic"); - } - if (startElement.tagName === "U" || endElement.tagName === "U") { - types.push("underline"); - } - if (startElement.tagName === "S" || endElement.tagName === "S") { - types.push("strike"); - } - if (startElement.tagName === "MARK" || endElement.tagName === "MARK") { - types.push("mark"); - } - if (startElement.tagName === "SUP" || endElement.tagName === "SUP") { - types.push("sup"); - } - if (startElement.tagName === "SUB" || endElement.tagName === "SUB") { - types.push("sub"); - } - if (startElement.tagName === "KBD" || endElement.tagName === "KBD") { - types.push("kbd"); - } - if (startElement.tagName === "SPAN" || endElement.tagName === "SPAN") { - const startType = startElement.getAttribute("data-type"); - const endType = endElement.getAttribute("data-type"); - if (startType === "tag" || endType === "tag") { - types.push("tag"); - } else if (startType === "a" || endType === "a") { - types.push("link"); - } else if (startType === "block-ref" || endType === "block-ref") { - types.push("blockRef"); - } else if (startType === "file-annotation-ref" || endType === "file-annotation-ref") { - types.push("fileAnnotationRef"); - } else if (startType === "inline-math") { - types.push("inline-math"); + range.cloneContents().childNodes.forEach((item:HTMLElement) => { + if (item.nodeType !== 3) { + types = types.concat(item.getAttribute("data--type").split(" ")); } - } - if (startElement.tagName === "CODE" || endElement.tagName === "CODE") { - types.push("inline-code"); - } + }); + types = [...new Set(types)]; return types; } private genItem(protyle: IProtyle, menuItem: IMenuItem) { let menuItemObj; switch (menuItem.name) { - case "bold": - case "italic": - case "strike": - case "inline-code": + case "strong": + case "em": + case "s": + case "code": case "mark": case "tag": - case "underline": + case "u": case "sup": case "sub": case "kbd": case "inline-math": menuItemObj = new ToolbarItem(protyle, menuItem); break; - case "blockRef": + case "block-ref": menuItemObj = new BlockRef(protyle, menuItem); break; case "|": menuItemObj = new Divider(); break; - case "font": + case "text": menuItemObj = new Font(protyle, menuItem); break; - case "link": + case "a": menuItemObj = new Link(protyle, menuItem); break; } @@ -267,7 +231,54 @@ export class Toolbar { }); } - public async setInlineMark(protyle: IProtyle, type: string, action: "remove" | "add" | "range" | "toolbar", focusAdd = false) { + public setInlineMark(protyle: IProtyle, type: string, action: "remove" | "add" | "range" | "toolbar", focusAdd = false) { + const nodeElement = hasClosestBlock(this.range.startContainer); + if (!nodeElement) { + return; + } + const wbrElement = document.createElement("wbr"); + const html = nodeElement.outerHTML; + + if (this.range.startOffset === 0 && !hasPreviousSibling(this.range.startContainer)) { + this.range.setStartBefore(this.range.startContainer.parentElement) + } + if (this.range.endOffset === this.range.endContainer.textContent.length && !hasNextSibling(this.range.endContainer)) { + this.range.setEndAfter(this.range.endContainer.parentElement) + } + const actionBtn = action === "toolbar" ? this.element.querySelector(`[data-type="${type}"]`) : undefined; + const contents = this.range.extractContents(); + this.range.insertNode(wbrElement); + const newNodes: Node[] = []; + contents.childNodes.forEach((item: HTMLElement) => { + if (item.nodeType === 3) { + if (item.textContent !== "") { + const inlineElement = document.createElement("span"); + inlineElement.setAttribute("data-type", type); + inlineElement.appendChild(item); + newNodes.push(inlineElement); + } + } else { + const types = (item.getAttribute("data-type") || "").split(" "); + types.push(type); + item.setAttribute("data-type", types.join(" ")); + newNodes.push(item); + } + }); + newNodes.forEach((item, index) => { + this.range.insertNode(item); + if (index === 0) { + this.range.setStart(item.firstChild, 0); + } + if (index === newNodes.length - 1) { + this.range.setEnd(item.lastChild, item.lastChild.textContent.length); + } + }); + nodeElement.setAttribute("updated", dayjs().format("YYYYMMDDHHmmss")); + updateTransaction(protyle, nodeElement.getAttribute("data-node-id"), nodeElement.outerHTML, html); + wbrElement.remove(); + } + + public async setInlineMark1(protyle: IProtyle, type: string, action: "remove" | "add" | "range" | "toolbar", focusAdd = false) { const nodeElement = hasClosestBlock(this.range.startContainer); if (!nodeElement) { return; @@ -281,7 +292,7 @@ export class Toolbar { return; } // 对已有字体样式的文字再次添加字体样式 - if (focusAdd && action === "add" && types.includes("bold") && this.range.startContainer.nodeType === 3 && + if (focusAdd && action === "add" && types.includes("text") && this.range.startContainer.nodeType === 3 && this.range.startContainer.parentNode.isSameNode(this.range.endContainer.parentNode)) { return; } diff --git a/app/src/protyle/util/Options.ts b/app/src/protyle/util/Options.ts index c97208f8c..bdf7438de 100644 --- a/app/src/protyle/util/Options.ts +++ b/app/src/protyle/util/Options.ts @@ -78,28 +78,28 @@ export class Options { mode: "both", }, toolbar: isMobile() ? [ - "blockRef", - "link", + "block-ref", + "a", "|", - "bold", - "italic", - "underline", - "strike", + "strong", + "em", + "u", + "s", "mark", "|", "tag", - "inline-code", + "code", "inline-math", "|", - "font", + "text", ] : [ - "blockRef", - "link", + "block-ref", + "a", "|", - "bold", - "italic", - "underline", - "strike", + "strong", + "em", + "u", + "s", "mark", "|", "sup", @@ -107,10 +107,10 @@ export class Options { "kbd", "|", "tag", - "inline-code", + "code", "inline-math", "|", - "font", + "text", ], typewriterMode: false, upload: { @@ -145,79 +145,92 @@ export class Options { private mergeToolbar(toolbar: Array) { const toolbarItem: IMenuItem [] = [{ - name: "blockRef", + name: "block-ref", hotkey: window.siyuan.config.keymap.editor.insert.blockRef.custom, + lang: "blockRef", icon: "iconGraph", tipPosition: "ne", }, { - name: "link", + name: "a", hotkey: window.siyuan.config.keymap.editor.insert.link.custom, + lang: "link", icon: "iconLink", tipPosition: "n", }, { - name: "bold", + name: "strong", + lang: "bold", hotkey: window.siyuan.config.keymap.editor.insert.bold.custom, icon: "iconBold", tipPosition: "n", }, { - name: "italic", + name: "em", + lang: "italic", hotkey: window.siyuan.config.keymap.editor.insert.italic.custom, icon: "iconItalic", tipPosition: "n", }, { - name: "underline", + name: "u", + lang: "underline", hotkey: window.siyuan.config.keymap.editor.insert.underline.custom, icon: "iconUnderline", tipPosition: "n", }, { - name: "strike", + name: "s", + lang: "strike", hotkey: window.siyuan.config.keymap.editor.insert.strike.custom, icon: "iconStrike", tipPosition: "n", }, { name: "mark", + lang: "mark", hotkey: window.siyuan.config.keymap.editor.insert.mark.custom, icon: "iconMark", tipPosition: "n", }, { name: "sup", + lang: "sup", hotkey: window.siyuan.config.keymap.editor.insert.sup.custom, icon: "iconSup", tipPosition: "n", }, { name: "sub", + lang: "sub", hotkey: window.siyuan.config.keymap.editor.insert.sub.custom, icon: "iconSub", tipPosition: "n", }, { name: "kbd", + lang: "kbd", hotkey: window.siyuan.config.keymap.editor.insert.kbd.custom, icon: "iconKeymap", tipPosition: "n", }, { name: "tag", + lang: "tag", hotkey: window.siyuan.config.keymap.editor.insert.tag.custom, icon: "iconTags", tipPosition: "n", }, { - name: "inline-code", + name: "code", + lang: "inline-code", hotkey: window.siyuan.config.keymap.editor.insert["inline-code"].custom, icon: "iconInlineCode", tipPosition: "n", }, { name: "inline-math", + lang: "inline-math", hotkey: window.siyuan.config.keymap.editor.insert["inline-math"].custom, icon: "iconMath", tipPosition: "n", }, { - name: "font", + name: "text", + lang: "font", hotkey: window.siyuan.config.keymap.editor.insert.font.custom, icon: "iconFont", tipPosition: "n", }, { name: "|", - } - ]; + }]; const toolbarResult: IMenuItem[] = []; toolbar.forEach((menuItem: IMenuItem) => { let currentMenuItem = menuItem; diff --git a/app/src/types/protyle.d.ts b/app/src/types/protyle.d.ts index 7f6c8b551..8d7e17bf7 100644 --- a/app/src/types/protyle.d.ts +++ b/app/src/types/protyle.d.ts @@ -268,6 +268,7 @@ interface IUpload { interface IMenuItem { /** 唯一标示 */ name: string; + lang?: string; /** svg 图标 */ icon?: string; /** 提示 */