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 { copySubMenu, movePathToMenu, openFileAttr, openFileWechatNotify, } from "../../menus/commonMenuItem"; import {Constants} from "../../constants"; import {hasClosestByClassName} from "../util/hasClosest"; import {matchHotKey} from "../util/hotKey"; import {updateHotkeyTip, writeText} from "../util/compatibility"; import {escapeHtml} from "../../util/escape"; import * as dayjs from "dayjs"; import {setPanelFocus} from "../../layout/util"; import {updatePanelByEditor} from "../../editor/util"; import {openBacklink, openGraph, openOutline} from "../../layout/dock/util"; import {setTitle} from "../../dialog/processSystem"; import {getNoContainerElement} from "../wysiwyg/getBlock"; import {commonHotkey} from "../wysiwyg/commonHotkey"; import {code160to32} from "../util/code160to32"; import {deleteFile} from "../../editor/deleteFile"; import {genEmptyElement} from "../../block/util"; import {transaction} from "../wysiwyg/transaction"; 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"); } this.element.innerHTML = `
`; this.editElement = this.element.querySelector(".protyle-title__input"); this.editElement.addEventListener("paste", (event: ClipboardEvent) => { event.stopPropagation(); event.preventDefault(); const range = getEditorRange(this.editElement); range.deleteContents(); range.insertNode(document.createTextNode(replaceFileName(event.clipboardData.getData("text/plain")))); range.collapse(false); this.rename(protyle); }); this.editElement.addEventListener("click", () => { if (protyle.model) { setPanelFocus(protyle.model.element.parentElement.parentElement); updatePanelByEditor(protyle, false); } protyle.toolbar?.element.classList.add("fn__none"); }); this.editElement.addEventListener("input", (event: InputEvent) => { if (event.isComposing) { return; } 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", (event: KeyboardEvent) => { if (event.isComposing) { return; } if (commonHotkey(protyle, event)) { return true; } if (event.key === "ArrowDown") { 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 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, protyle.block.rootID); }); event.preventDefault(); event.stopPropagation(); } else if (matchHotKey("⌘A", event)) { getEditorRange(this.editElement).selectNodeContents(this.editElement); event.preventDefault(); event.stopPropagation(); } else if (matchHotKey(window.siyuan.config.keymap.editor.general.copyBlockRef.custom, event)) { writeText(`((${protyle.block.rootID} '${this.editElement.textContent.replace(/'/g, "'")}'))`); event.preventDefault(); event.stopPropagation(); } else if (matchHotKey(window.siyuan.config.keymap.editor.general.copyID.custom, event)) { writeText(protyle.block.rootID); event.preventDefault(); event.stopPropagation(); } else if (matchHotKey(window.siyuan.config.keymap.editor.general.copyBlockEmbed.custom, event)) { writeText(`{{select * from blocks where id='${protyle.block.rootID}'}}`); event.preventDefault(); event.stopPropagation(); } else if (matchHotKey(window.siyuan.config.keymap.editor.general.copyProtocol.custom, event)) { writeText(`siyuan://blocks/${protyle.block.rootID}`); 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, protyle.block.rootID); }); } else { const iconRect = iconElement.getBoundingClientRect(); this.renderMenu(protyle, {x: iconRect.left, y: iconRect.top + 14}); } }); this.element.addEventListener("contextmenu", (event) => { if (getSelection().rangeCount === 0) { this.renderMenu(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({ 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({ 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({ 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({ label: window.siyuan.languages.paste, accelerator: "⌘V", click: () => { focusByRange(getEditorRange(this.editElement)); document.execCommand("paste"); setTimeout(() => { this.rename(protyle); }, Constants.TIMEOUT_INPUT); } }).element); window.siyuan.menus.menu.append(new MenuItem({ label: window.siyuan.languages.selectAll, 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) => { const attrBookmarkElement = hasClosestByClassName(event.target, "protyle-attr--bookmark"); if (attrBookmarkElement) { openFileAttr(response.data.ial, protyle.block.rootID, "bookmark"); event.stopPropagation(); return; } const attrNameElement = hasClosestByClassName(event.target, "protyle-attr--name"); if (attrNameElement) { openFileAttr(response.data.ial, protyle.block.rootID, "name"); event.stopPropagation(); return; } const attrAliasElement = hasClosestByClassName(event.target, "protyle-attr--alias"); if (attrAliasElement) { openFileAttr(response.data.ial, protyle.block.rootID, "alias"); event.stopPropagation(); return; } const attrMemoElement = hasClosestByClassName(event.target, "protyle-attr--memo"); if (attrMemoElement) { openFileAttr(response.data.ial, protyle.block.rootID, "memo"); event.stopPropagation(); return; } }); }); } private rename(protyle: IProtyle) { clearTimeout(this.timeout); if (!validateName(this.editElement.textContent)) { // 字数过长会导致滚动 const offset = getSelectionOffset(this.editElement); this.setTitle(this.editElement.textContent.substring(0, Constants.SIZE_TITLE)); focusByOffset(this.editElement, offset.start, offset.end); return false; } this.timeout = window.setTimeout(() => { const fileName = replaceFileName(this.editElement.textContent); fetchPost("/api/filetree/renameDoc", { notebook: protyle.notebookId, path: protyle.path, title: fileName, }); this.setTitle(fileName); setTitle(fileName); }, Constants.TIMEOUT_INPUT); } private renderMenu(protyle: IProtyle, position: { x: number, y: number }) { fetchPost("/api/block/getDocInfo", { id: protyle.block.rootID }, (response) => { window.siyuan.menus.menu.remove(); window.siyuan.menus.menu.append(new MenuItem({ label: window.siyuan.languages.copy, icon: "iconCopy", type: "submenu", submenu: copySubMenu(protyle.block.rootID) }).element); if (!protyle.disabled) { window.siyuan.menus.menu.append(movePathToMenu(protyle.notebookId, protyle.path)); window.siyuan.menus.menu.append(new MenuItem({ icon: "iconTrashcan", label: window.siyuan.languages.delete, click: () => { deleteFile(protyle.notebookId, protyle.path, escapeHtml(this.editElement.textContent)); } }).element); window.siyuan.menus.menu.append(new MenuItem({type: "separator"}).element); window.siyuan.menus.menu.append(new MenuItem({ label: window.siyuan.languages.attr, accelerator: window.siyuan.config.keymap.editor.general.attr.custom + "/" + updateHotkeyTip("⇧Click"), click() { openFileAttr(response.data.ial, protyle.block.rootID); } }).element); } window.siyuan.menus.menu.append(new MenuItem({type: "separator"}).element); window.siyuan.menus.menu.append(new MenuItem({ icon: "iconAlignCenter", label: window.siyuan.languages.outline, accelerator: window.siyuan.config.keymap.editor.general.outline.custom, click: () => { openOutline(protyle); } }).element); window.siyuan.menus.menu.append(new MenuItem({ icon: "iconLink", label: window.siyuan.languages.backlinks, accelerator: window.siyuan.config.keymap.editor.general.backlinks.custom, click: () => { openBacklink(protyle); } }).element); window.siyuan.menus.menu.append(new MenuItem({ icon: "iconGraph", label: window.siyuan.languages.graphView, accelerator: window.siyuan.config.keymap.editor.general.graphView.custom, click: () => { openGraph(protyle); } }).element); window.siyuan.menus.menu.append(new MenuItem({type: "separator"}).element); window.siyuan.menus.menu.append(new MenuItem({ label: window.siyuan.languages.wechatReminder, icon: "iconMp", click() { openFileWechatNotify(protyle); } }).element); window.siyuan.menus.menu.append(new MenuItem({type: "separator"}).element); window.siyuan.menus.menu.append(new MenuItem({ type: "readonly", label: `${window.siyuan.languages.modifiedAt} ${dayjs(response.data.ial.updated).format("YYYY-MM-DD HH:mm:ss")}