import { focusBlock, focusByOffset, focusByRange, focusByWbr, getEditorRange, getSelectionOffset, } from "../util/selection"; import {fetchPost} from "../../util/fetch"; import {replaceFileName, validateName} from "../../editor/rename"; import {MenuItem} from "../../menus/Menu"; import {openFileAttr,} from "../../menus/commonMenuItem"; import {Constants} from "../../constants"; import {matchHotKey} from "../util/hotKey"; import {isMac, readText} from "../util/compatibility"; import * as dayjs from "dayjs"; /// #if !MOBILE import {openFileById} from "../../editor/util"; /// #endif import {setTitle} from "../../dialog/processSystem"; import {getContenteditableElement, getNoContainerElement} from "../wysiwyg/getBlock"; import {commonHotkey} from "../wysiwyg/commonHotkey"; import {code160to32} from "../util/code160to32"; import {genEmptyElement} from "../../block/util"; import {transaction} from "../wysiwyg/transaction"; import {hideTooltip} from "../../dialog/tooltip"; import {commonClick} from "../wysiwyg/commonClick"; import {openTitleMenu} from "./openTitleMenu"; import {electronUndo} from "../undo"; import {enableLuteMarkdownSyntax, restoreLuteMarkdownSyntax} from "../util/paste"; export class Title { public element: HTMLElement; public editElement: HTMLElement; private timeout: number; constructor(protyle: IProtyle) { this.element = document.createElement("div"); this.element.className = "protyle-title"; if (window.siyuan.config.editor.displayBookmarkIcon) { this.element.classList.add("protyle-wysiwyg--attr"); } if (protyle.options.render?.titleShowTop) { this.element.innerHTML = '
'; } else { // 标题内需要一个空格,避免首次加载出现`请输入文档名`干扰 this.element.innerHTML = `
`; this.editElement = this.element.querySelector(".protyle-title__input"); this.editElement.addEventListener("paste", (event: ClipboardEvent) => { event.stopPropagation(); event.preventDefault(); // 不能使用 range.insertNode,否则无法撤销 let text = event.clipboardData.getData("text/siyuan"); if (text) { try { JSON.parse(text); text = event.clipboardData.getData("text/plain"); } catch (e) { // 不为数据库,保持 text 不变 } text = protyle.lute.BlockDOM2Content(text); } else { text = event.clipboardData.getData("text/plain"); } // 阻止右键复制菜单报错 setTimeout(function () { document.execCommand("insertText", false, replaceFileName(text)); }, 0); this.rename(protyle); }); this.editElement.addEventListener("click", () => { protyle.toolbar?.element.classList.add("fn__none"); }); this.editElement.addEventListener("input", (event: InputEvent) => { if (event.isComposing) { return; } if (this.editElement.textContent === "") { this.editElement.querySelectorAll("br").forEach(item => { item.remove(); }); } this.rename(protyle); }); this.editElement.addEventListener("compositionend", () => { this.rename(protyle); }); this.editElement.addEventListener("drop", (event: DragEvent) => { // https://ld246.com/article/1661911210429 event.stopPropagation(); event.preventDefault(); }); this.editElement.addEventListener("keydown", async (event: KeyboardEvent) => { if (event.isComposing) { return; } if (commonHotkey(protyle, event)) { return true; } if (matchHotKey("⇧⌘V", event)) { event.preventDefault(); event.stopPropagation(); let textPlain = await readText() || ""; if (textPlain) { // 对 HTML 标签进行内部转义,避免被 Lute 解析以后变为小写 https://github.com/siyuan-note/siyuan/issues/10620 textPlain = textPlain.replace(//g, ";;;gt;;;"); enableLuteMarkdownSyntax(protyle); let content = protyle.lute.BlockDOM2EscapeMarkerContent(protyle.lute.Md2BlockDOM(textPlain)); restoreLuteMarkdownSyntax(protyle); // 移除 ;;;lt;;; 和 ;;;gt;;; 转义及其包裹的内容 content = content.replace(/;;;lt;;;[^;]+;;;gt;;;/g, ""); document.execCommand("insertText", false, replaceFileName(content)); this.rename(protyle); } return; } if (matchHotKey(window.siyuan.config.keymap.general.enterBack.custom, event)) { const ids = protyle.path.split("/"); if (ids.length > 2) { /// #if !MOBILE openFileById({ app: protyle.app, id: ids[ids.length - 2], action: [Constants.CB_GET_FOCUS, Constants.CB_GET_SCROLL] }); /// #endif } event.preventDefault(); event.stopPropagation(); return; } if (electronUndo(event)) { return; } if (event.key === "ArrowDown") { const rects = getSelection().getRangeAt(0).getClientRects(); // https://github.com/siyuan-note/siyuan/issues/11729 if (rects.length === 0 // 标题为空时时 || this.editElement.getBoundingClientRect().bottom - rects[rects.length - 1].bottom < 25) { const noContainerElement = getNoContainerElement(protyle.wysiwyg.element.firstElementChild); // https://github.com/siyuan-note/siyuan/issues/4923 if (noContainerElement) { focusBlock(noContainerElement, protyle.wysiwyg.element); } event.preventDefault(); event.stopPropagation(); } } else if (event.key === "Enter") { const editElement = getContenteditableElement(protyle.wysiwyg.element.firstElementChild); if (editElement && editElement.textContent === "" && editElement.getAttribute("placeholder")) { // 配合提示文本使用,避免提示文本挤压到第二个块中 focusBlock(protyle.wysiwyg.element.firstElementChild, protyle.wysiwyg.element); } else { const newId = Lute.NewNodeID(); const newElement = genEmptyElement(false, true, newId); protyle.wysiwyg.element.insertAdjacentElement("afterbegin", newElement); focusByWbr(newElement, protyle.toolbar.range || getEditorRange(newElement)); transaction(protyle, [{ action: "insert", data: newElement.outerHTML, id: newId, parentID: protyle.block.parentID }], [{ action: "delete", id: newId, }]); } event.preventDefault(); event.stopPropagation(); } else if (matchHotKey(window.siyuan.config.keymap.editor.general.attr.custom, event)) { fetchPost("/api/block/getDocInfo", { id: protyle.block.rootID }, (response) => { openFileAttr(response.data.ial, "bookmark", protyle); }); event.preventDefault(); event.stopPropagation(); } else if (matchHotKey("⌘A", event)) { getEditorRange(this.editElement).selectNodeContents(this.editElement); event.preventDefault(); event.stopPropagation(); } }); const iconElement = this.element.querySelector(".protyle-title__icon"); iconElement.addEventListener("click", () => { if (window.siyuan.shiftIsPressed) { fetchPost("/api/block/getDocInfo", { id: protyle.block.rootID }, (response) => { openFileAttr(response.data.ial, "bookmark", protyle); }); } else { const iconRect = iconElement.getBoundingClientRect(); openTitleMenu(protyle, {x: iconRect.left, y: iconRect.bottom}); } }); this.element.addEventListener("contextmenu", (event) => { if (event.shiftKey) { return; } if (getSelection().rangeCount === 0) { openTitleMenu(protyle, {x: event.clientX, y: event.clientY}); return; } protyle.toolbar?.element.classList.add("fn__none"); window.siyuan.menus.menu.remove(); const range = getEditorRange(this.editElement); if (range.toString() !== "") { window.siyuan.menus.menu.append(new MenuItem({ id: "copy", icon: "iconCopy", accelerator: "⌘C", label: window.siyuan.languages.copy, click: () => { focusByRange(getEditorRange(this.editElement)); document.execCommand("copy"); } }).element); window.siyuan.menus.menu.append(new MenuItem({ id: "cut", icon: "iconCut", accelerator: "⌘X", label: window.siyuan.languages.cut, click: () => { focusByRange(getEditorRange(this.editElement)); document.execCommand("cut"); setTimeout(() => { this.rename(protyle); }, Constants.TIMEOUT_INPUT); } }).element); window.siyuan.menus.menu.append(new MenuItem({ id: "delete", icon: "iconTrashcan", accelerator: "⌫", label: window.siyuan.languages.delete, click: () => { const range = getEditorRange(this.editElement); range.extractContents(); focusByRange(range); setTimeout(() => { this.rename(protyle); }, Constants.TIMEOUT_INPUT); } }).element); } window.siyuan.menus.menu.append(new MenuItem({ id: "paste", label: window.siyuan.languages.paste, icon: "iconPaste", accelerator: "⌘V", click: async () => { focusByRange(getEditorRange(this.editElement)); if (document.queryCommandSupported("paste")) { document.execCommand("paste"); } else { try { const text = await readText() || ""; document.execCommand("insertText", false, replaceFileName(text)); this.rename(protyle); } catch (e) { console.log(e); } } } }).element); window.siyuan.menus.menu.append(new MenuItem({ id: "pasteAsPlainText", label: window.siyuan.languages.pasteAsPlainText, accelerator: "⇧⌘V", click: async () => { let textPlain = await readText() || ""; textPlain = textPlain.replace(//g, ";;;gt;;;"); enableLuteMarkdownSyntax(protyle); let content = protyle.lute.BlockDOM2EscapeMarkerContent(protyle.lute.Md2BlockDOM(textPlain)); restoreLuteMarkdownSyntax(protyle); // 移除 ;;;lt;;; 和 ;;;gt;;; 转义及其包裹的内容 content = content.replace(/;;;lt;;;[^;]+;;;gt;;;/g, ""); document.execCommand("insertText", false, replaceFileName(content)); this.rename(protyle); } }).element); window.siyuan.menus.menu.append(new MenuItem({ id: "selectAll", label: window.siyuan.languages.selectAll, icon: "iconSelect", accelerator: "⌘A", click: () => { range.selectNodeContents(this.editElement); focusByRange(range); } }).element); window.siyuan.menus.menu.popup({x: event.clientX, y: event.clientY}); }); } this.element.querySelector(".protyle-attr").addEventListener("click", (event: MouseEvent & { target: HTMLElement }) => { fetchPost("/api/block/getDocInfo", { id: protyle.block.rootID }, (response) => { commonClick(event, protyle, response.data.ial); }); }); } private rename(protyle: IProtyle) { clearTimeout(this.timeout); if (!validateName(this.editElement.textContent, this.editElement)) { // 字数过长会导致滚动 const offset = getSelectionOffset(this.editElement); this.setTitle(this.editElement.textContent.substring(0, Constants.SIZE_TITLE)); focusByOffset(this.editElement, offset.start, offset.end); return false; } hideTooltip(); this.timeout = window.setTimeout(() => { const fileName = replaceFileName(this.editElement.textContent); fetchPost("/api/filetree/renameDoc", { notebook: protyle.notebookId, path: protyle.path, title: fileName, }); if (fileName !== this.editElement.textContent) { const offset = getSelectionOffset(this.editElement); this.setTitle(fileName); focusByOffset(this.editElement, offset.start, offset.end); } setTitle(fileName); }, Constants.TIMEOUT_INPUT); } public setTitle(title: string) { /// #if MOBILE if (this.editElement) { if (code160to32(title) !== code160to32(this.editElement.textContent)) { this.editElement.textContent = title === window.siyuan.languages.untitled ? "" : title; } } else { const inputElement = document.getElementById("toolbarName") as HTMLInputElement; if (code160to32(title) !== code160to32(inputElement.value)) { inputElement.value = title === window.siyuan.languages.untitled ? "" : title; } } /// #else if (code160to32(title) !== code160to32(this.editElement.textContent)) { this.editElement.textContent = title === window.siyuan.languages.untitled ? "" : title; } /// #endif } public render(protyle: IProtyle, response: IWebSocketData) { if (this.element.getAttribute("data-render") === "true" && this.element.dataset.nodeId === protyle.block.rootID) { return false; } if (protyle.options.render.hideTitleOnZoom) { if (protyle.block.showAll) { this.element.classList.add("fn__none"); } else { this.element.classList.remove("fn__none"); } } this.element.setAttribute("data-node-id", protyle.block.rootID); if (response.data.ial[Constants.CUSTOM_RIFF_DECKS]) { this.element.setAttribute(Constants.CUSTOM_RIFF_DECKS, response.data.ial[Constants.CUSTOM_RIFF_DECKS]); } protyle.background?.render(response.data.ial, protyle.block.rootID); protyle.wysiwyg.renderCustom(response.data.ial); this.element.setAttribute("data-render", "true"); this.setTitle(response.data.ial.title); let nodeAttrHTML = ""; if (response.data.ial.bookmark) { nodeAttrHTML += `
${Lute.EscapeHTMLStr(response.data.ial.bookmark)}
`; } if (response.data.ial.name) { nodeAttrHTML += `
${Lute.EscapeHTMLStr(response.data.ial.name)}
`; } if (response.data.ial.alias) { nodeAttrHTML += `
${Lute.EscapeHTMLStr(response.data.ial.alias)}
`; } if (response.data.ial.memo) { nodeAttrHTML += `
`; } if (response.data.ial["custom-avs"]) { let avTitle = ""; response.data.attrViews.forEach((item: { id: string, name: string }) => { avTitle += `${item.name} `; }); if (avTitle) { avTitle = avTitle.substring(0, avTitle.length - 6); } nodeAttrHTML += `
${avTitle}
`; } this.element.querySelector(".protyle-attr").innerHTML = nodeAttrHTML; if (response.data.refCount !== 0) { this.element.querySelector(".protyle-attr").insertAdjacentHTML("beforeend", `
${response.data.refCount}
`); } // 存在设置新建文档名模板,不能使用 Untitled 进行判断,https://ld246.com/article/1649301009888 if (this.editElement && new Date().getTime() - dayjs(response.data.id.split("-")[0]).toDate().getTime() < 2000) { const range = this.editElement.ownerDocument.createRange(); range.selectNodeContents(this.editElement); focusByRange(range); } } }