diff --git a/app/src/assets/scss/_typography.scss b/app/src/assets/scss/_typography.scss index fb38c0715..c19656d8f 100644 --- a/app/src/assets/scss/_typography.scss +++ b/app/src/assets/scss/_typography.scss @@ -97,6 +97,11 @@ } } + span[data-type~="inline-memo"] { + background-color: var(--b3-card-info-background); + border-bottom: 2px solid var(--b3-card-info-color); + } + blockquote, .bq { padding: 4px; diff --git a/app/src/block/popover.ts b/app/src/block/popover.ts index 6a10e4d22..8f9338346 100644 --- a/app/src/block/popover.ts +++ b/app/src/block/popover.ts @@ -11,9 +11,10 @@ export const initBlockPopover = () => { const aElement = hasClosestByAttribute(event.target, "data-type", "a", true) || hasClosestByAttribute(event.target, "data-type", "tab-header") || hasClosestByClassName(event.target, "emojis__item") || - hasClosestByClassName(event.target, "emojis__type"); + hasClosestByClassName(event.target, "emojis__type") || + hasClosestByAttribute(event.target, "data-type", "inline-memo"); if (aElement) { - let tip = aElement.getAttribute("aria-label"); + let tip = aElement.getAttribute("aria-label") || aElement.getAttribute("data-inline-memo-content"); // 折叠块标文案替换 if (hasClosestByAttribute(event.target, "data-type", "fold", true)) { tip = window.siyuan.languages.fold; diff --git a/app/src/constants.ts b/app/src/constants.ts index c15fe5a37..996cc3429 100644 --- a/app/src/constants.ts +++ b/app/src/constants.ts @@ -171,6 +171,7 @@ export abstract class Constants { sub: {default: "⌘J", custom: "⌘J"}, bold: {default: "⌘B", custom: "⌘B"}, "inline-math": {default: "⌘M", custom: "⌘M"}, + memo: {default: "⌥⌘M", custom: "⌥⌘M"}, underline: {default: "⌘U", custom: "⌘U"}, italic: {default: "⌘I", custom: "⌘I"}, mark: {default: "⌘E", custom: "⌘E"}, @@ -397,7 +398,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[] = ["block-ref", "kbd", "text", "file-annotation-ref", "a", "strong", "em", "u", "s", "mark", "sup", "sub", "tag", "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", "inline-memo"]; 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/toolbar/InlineMath.ts b/app/src/protyle/toolbar/InlineMath.ts index f219b9c1d..db257c9bd 100644 --- a/app/src/protyle/toolbar/InlineMath.ts +++ b/app/src/protyle/toolbar/InlineMath.ts @@ -3,7 +3,6 @@ import * as dayjs from "dayjs"; import {updateTransaction} from "../wysiwyg/transaction"; import {hasClosestBlock} from "../util/hasClosest"; import {hasNextSibling, hasPreviousSibling} from "../wysiwyg/getBlock"; -import {focusByRange, focusByWbr} from "../util/selection"; import {mathRender} from "../markdown/mathRender"; export class InlineMath extends ToolbarItem { @@ -49,24 +48,3 @@ export class InlineMath extends ToolbarItem { }); } } - -export const removeLink = (linkElement: HTMLElement, range: Range) => { - const types = linkElement.getAttribute("data-type").split(" "); - if (types.length === 1) { - const linkParentElement = linkElement.parentElement; - linkElement.outerHTML = linkElement.innerHTML + ""; - focusByWbr(linkParentElement, range); - } else { - types.find((itemType, index) => { - if ("a" === itemType) { - types.splice(index, 1); - return true; - } - }); - linkElement.setAttribute("data-type", types.join(" ")); - linkElement.removeAttribute("data-href"); - range.selectNodeContents(linkElement); - range.collapse(false); - focusByRange(range); - } -}; diff --git a/app/src/protyle/toolbar/InlineMemo.ts b/app/src/protyle/toolbar/InlineMemo.ts new file mode 100644 index 000000000..6b333e6ca --- /dev/null +++ b/app/src/protyle/toolbar/InlineMemo.ts @@ -0,0 +1,52 @@ +import {ToolbarItem} from "./ToolbarItem"; +import * as dayjs from "dayjs"; +import {updateTransaction} from "../wysiwyg/transaction"; +import {hasClosestBlock, hasClosestByAttribute} from "../util/hasClosest"; +import {hasNextSibling, hasPreviousSibling} from "../wysiwyg/getBlock"; + +export class InlineMemo extends ToolbarItem { + public element: HTMLElement; + + constructor(protyle: IProtyle, menuItem: IMenuItem) { + super(protyle, menuItem); + this.element.addEventListener("click", async (event: MouseEvent & { changedTouches: MouseEvent[] }) => { + protyle.toolbar.element.classList.add("fn__none"); + event.stopPropagation(); + + const range = protyle.toolbar.range; + const nodeElement = hasClosestBlock(range.startContainer); + if (!nodeElement) { + return; + } + const memoElement = hasClosestByAttribute(range.startContainer, "data-type", "inline-memo"); + if (memoElement) { + protyle.toolbar.showRender(protyle, memoElement); + return; + } + + if (range.toString() === "") { + return; + } + + if (!["DIV", "TD", "TH"].includes(range.startContainer.parentElement.tagName) && range.startOffset === 0 && !hasPreviousSibling(range.startContainer)) { + range.setStartBefore(range.startContainer.parentElement); + } + if (!["DIV", "TD", "TH"].includes(range.endContainer.parentElement.tagName) && range.endOffset === range.endContainer.textContent.length && !hasNextSibling(range.endContainer)) { + range.setEndAfter(range.endContainer.parentElement); + } + const wbrElement = document.createElement("wbr"); + 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); + 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/ToolbarItem.ts b/app/src/protyle/toolbar/ToolbarItem.ts index 9cd4963ee..f5847ba46 100644 --- a/app/src/protyle/toolbar/ToolbarItem.ts +++ b/app/src/protyle/toolbar/ToolbarItem.ts @@ -12,7 +12,7 @@ export class ToolbarItem { this.element.setAttribute("data-type", menuItem.name); this.element.setAttribute("aria-label", tip + hotkey); this.element.innerHTML = ``; - if (["text", "a", "block-ref", "inline-math"].includes(menuItem.name)) { + if (["text", "a", "block-ref", "inline-math", "inline-memo"].includes(menuItem.name)) { return; } this.element.addEventListener(getEventName(), (event) => { diff --git a/app/src/protyle/toolbar/index.ts b/app/src/protyle/toolbar/index.ts index afa36e265..95d4d245c 100644 --- a/app/src/protyle/toolbar/index.ts +++ b/app/src/protyle/toolbar/index.ts @@ -40,6 +40,7 @@ import {electronUndo} from "../undo"; import {previewTemplate} from "./util"; import {showMessage} from "../../dialog/message"; import {InlineMath} from "./InlineMath"; +import {InlineMemo} from "./InlineMemo"; export class Toolbar { public element: HTMLElement; @@ -122,7 +123,7 @@ export class Toolbar { }); const types = this.getCurrentType(); types.forEach(item => { - if (["a", "block-ref", "text", "file-annotation-ref", "inline-math", ""].includes(item)) { + if (["a", "block-ref", "text", "file-annotation-ref", "inline-math", "inline-memo", ""].includes(item)) { return; } this.element.querySelector(`[data-type="${item}"]`).classList.add("protyle-toolbar__item--current"); @@ -191,6 +192,9 @@ export class Toolbar { case "inline-math": menuItemObj = new InlineMath(protyle, menuItem); break; + case "inline-memo": + menuItemObj = new InlineMemo(protyle, menuItem); + break; case "|": menuItemObj = new Divider(); break; @@ -636,6 +640,8 @@ export class Toolbar { } if (type === "NodeBlockQueryEmbed") { title = window.siyuan.languages.blockEmbed; + } else if (type === "inline-memo") { + title = window.siyuan.languages.memo; } const isPin = this.subElement.querySelector('[data-type="pin"]')?.classList.contains("block__icon--active"); const pinData: IObject = {}; @@ -660,7 +666,7 @@ export class Toolbar { - + @@ -680,7 +686,7 @@ export class Toolbar { return; } if (this.subElement.clientHeight <= window.innerHeight - nodeRect.bottom || this.subElement.clientHeight <= nodeRect.top) { - if (type === "inline-math") { + if (type === "inline-math" || type === "inline-memo") { setPosition(this.subElement, nodeRect.left, nodeRect.bottom, nodeRect.height); } else { setPosition(this.subElement, nodeRect.left + (nodeRect.width - this.subElement.clientWidth) / 2, nodeRect.bottom, nodeRect.height); @@ -786,6 +792,8 @@ export class Toolbar { const textElement = this.subElement.querySelector(".b3-text-field") as HTMLTextAreaElement; if (type === "NodeHTMLBlock") { textElement.value = Lute.UnEscapeHTMLStr(renderElement.querySelector("protyle-html").getAttribute("data-content") || ""); + } else if (type === "inline-memo") { + textElement.value = Lute.UnEscapeHTMLStr(renderElement.getAttribute("data-inline-memo-content") || ""); } else { const switchElement = this.subElement.querySelector(".b3-switch") as HTMLInputElement; if (nodeElement.getAttribute("custom-heading-mode") === "1") { @@ -817,6 +825,8 @@ export class Toolbar { const target = event.target as HTMLTextAreaElement; if (type === "NodeHTMLBlock") { renderElement.querySelector("protyle-html").setAttribute("data-content", Lute.EscapeHTMLStr(target.value)); + } else if (type === "inline-memo") { + renderElement.setAttribute("data-inline-memo-content", Lute.EscapeHTMLStr(target.value)); } else { renderElement.setAttribute("data-content", Lute.EscapeHTMLStr(target.value)); renderElement.removeAttribute("data-render"); @@ -835,6 +845,8 @@ export class Toolbar { if (!this.subElement.querySelector('[data-type="refresh"]').classList.contains("block__icon--active")) { if (type === "NodeHTMLBlock") { renderElement.querySelector("protyle-html").setAttribute("data-content", Lute.EscapeHTMLStr(target.value)); + } else if (type === "inline-memo") { + renderElement.setAttribute("data-inline-memo-content", Lute.EscapeHTMLStr(target.value)); } else { renderElement.setAttribute("data-content", Lute.EscapeHTMLStr(target.value)); renderElement.removeAttribute("data-render"); diff --git a/app/src/protyle/util/Options.ts b/app/src/protyle/util/Options.ts index bdf7438de..15e1ff0e2 100644 --- a/app/src/protyle/util/Options.ts +++ b/app/src/protyle/util/Options.ts @@ -91,6 +91,7 @@ export class Options { "code", "inline-math", "|", + "inline-memo", "text", ] : [ "block-ref", @@ -110,6 +111,7 @@ export class Options { "code", "inline-math", "|", + "inline-memo", "text", ], typewriterMode: false, @@ -222,6 +224,12 @@ export class Options { hotkey: window.siyuan.config.keymap.editor.insert["inline-math"].custom, icon: "iconMath", tipPosition: "n", + }, { + name: "inline-memo", + lang: "memo", + hotkey: window.siyuan.config.keymap.editor.insert.memo.custom, + icon: "iconM", + tipPosition: "n", }, { name: "text", lang: "font", diff --git a/app/src/protyle/wysiwyg/index.ts b/app/src/protyle/wysiwyg/index.ts index 428cce0ac..eb8822b31 100644 --- a/app/src/protyle/wysiwyg/index.ts +++ b/app/src/protyle/wysiwyg/index.ts @@ -116,7 +116,7 @@ export class WYSIWYG { if (// 表格行内公式之前无法插入文字 https://github.com/siyuan-note/siyuan/issues/3908 inlineElement.tagName==="SPAN" && range.toString() === "" && range.startContainer.nodeType === 3 && - (currentTypes.includes("text") || currentTypes.includes("block-ref")|| currentTypes.includes("file-annotation-ref")|| currentTypes.includes("a")) && + (currentTypes.includes("inline-memo") || currentTypes.includes("text") || currentTypes.includes("block-ref")|| currentTypes.includes("file-annotation-ref")|| currentTypes.includes("a")) && !hasNextSibling(range.startContainer) && range.startContainer.textContent.length === range.startOffset && inlineElement.textContent.replace(Constants.ZWSP, "").length >= inputData.length // 为空的时候需要等于 ) { @@ -1090,6 +1090,10 @@ export class WYSIWYG { protyle.toolbar.showFileAnnotationRef(protyle, target); return false; } + if (types.includes("inline-memo")) { + protyle.toolbar.showRender(protyle, target); + return false; + } if (types.includes("a")) { linkMenu(protyle, target); if (target.getAttribute("data-href")?.startsWith("siyuan://blocks")) { diff --git a/app/src/protyle/wysiwyg/keydown.ts b/app/src/protyle/wysiwyg/keydown.ts index 303ef16d5..d812e0310 100644 --- a/app/src/protyle/wysiwyg/keydown.ts +++ b/app/src/protyle/wysiwyg/keydown.ts @@ -514,6 +514,9 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => { if (types.includes("block-ref")) { refMenu(protyle, inlineElement); return; + } else if (types.includes("inline-memo")) { + protyle.toolbar.showRender(protyle, inlineElement); + return; } else if (types.includes("file-annotation-ref")) { protyle.toolbar.showFileAnnotationRef(protyle, inlineElement); return; @@ -1180,7 +1183,7 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => { } if (matchHotKey(menuItem.hotkey, event)) { protyle.toolbar.range = getEditorRange(protyle.wysiwyg.element); - if (["text", "a", "block-ref", "inline-math"].includes(menuItem.name)) { + if (["text", "a", "block-ref", "inline-math", "inline-memo"].includes(menuItem.name)) { protyle.toolbar.element.querySelector(`[data-type="${menuItem.name}"]`).dispatchEvent(new CustomEvent("block-ref" === menuItem.name ? getEventName() : "click")); } else { protyle.toolbar.setInlineMark(protyle, menuItem.name, "range");