import {Constants} from "../../constants";
import {hasClosestBlock, hasClosestByAttribute, hasClosestByClassName, hasClosestByMatchTag} from "../util/hasClosest";
import {
focusBlock,
focusByRange,
focusByWbr,
getEditorRange,
getSelectionOffset,
getSelectionPosition
} from "../util/selection";
import {genHintItemHTML, hintEmbed, hintRef, hintSlash} from "./extend";
import {getSavePath} from "../../util/newFile";
import {upDownHint} from "../../util/upDownHint";
import {setPosition} from "../../util/setPosition";
import {getContenteditableElement, hasNextSibling, hasPreviousSibling} from "../wysiwyg/getBlock";
import {transaction, updateTransaction} from "../wysiwyg/transaction";
import {insertHTML} from "../util/insertHTML";
import {highlightRender} from "../render/highlightRender";
import {assetMenu, imgMenu} from "../../menus/protyle";
import {hideElements} from "../ui/hideElements";
import {fetchPost} from "../../util/fetch";
import {getDisplayName, pathPosix} from "../../util/pathName";
import {addEmoji, filterEmoji, lazyLoadEmoji, lazyLoadEmojiImg, unicode2Emoji} from "../../emoji";
import {blockRender} from "../render/blockRender";
import {uploadFiles} from "../upload";
/// #if !MOBILE
import {openFileById} from "../../editor/util";
/// #endif
import {openMobileFileById} from "../../mobile/editor";
import {processRender} from "../util/processCode";
import {AIChat} from "../../ai/chat";
import {isMobile} from "../../util/functions";
import {isIPhone, isNotCtrl, isOnlyMeta} from "../util/compatibility";
import {avRender} from "../render/av/render";
import {genIconHTML} from "../render/util";
export class Hint {
public timeId: number;
public element: HTMLDivElement;
public enableSlash = true;
private enableEmoji = true;
public enableExtend = false;
public splitChar = "";
public lastIndex = -1;
private source: THintSource;
constructor(protyle: IProtyle) {
this.element = document.createElement("div");
this.element.setAttribute("data-close", "false");
// height 402 根据 .emojis max-height+8 得来
this.element.setAttribute("style", `width:${Math.max(protyle.element.clientWidth / 2, 320)}px;`);
this.element.className = "protyle-hint b3-list b3-list--background fn__none";
this.element.addEventListener("click", (event) => {
const eventTarget = event.target as HTMLElement;
if (eventTarget.tagName === "INPUT") {
event.stopPropagation();
return;
}
const btnElement = hasClosestByMatchTag(eventTarget, "BUTTON");
if (btnElement && !btnElement.classList.contains("emojis__item") && !btnElement.classList.contains("emojis__type")) {
if (this.source !== "search") {
this.fill(decodeURIComponent(btnElement.getAttribute("data-value")), protyle, true, isOnlyMeta(event));
} else {
setTimeout(() => {
this.fill(decodeURIComponent(btnElement.getAttribute("data-value")), protyle, true, isNotCtrl(event));
}, 148); // 划选引用点击,需先重置 range
}
focusByRange(protyle.toolbar.range);
event.preventDefault();
event.stopPropagation(); // https://github.com/siyuan-note/siyuan/issues/3710
return;
}
const emojisContentElement = this.element.querySelector(".emojis__panel");
const typeElement = hasClosestByClassName(eventTarget, "emojis__type");
if (typeElement) {
const titleElement = emojisContentElement.querySelector(`[data-type="${typeElement.getAttribute("data-type")}"]`) as HTMLElement;
if (titleElement) {
const index = titleElement.nextElementSibling.getAttribute("data-index");
if (index) {
let html = "";
window.siyuan.emojis[parseInt(index)].items.forEach(emoji => {
html += ``;
});
titleElement.nextElementSibling.innerHTML = html;
titleElement.nextElementSibling.removeAttribute("data-index");
}
emojisContentElement.scrollTo({
top: titleElement.offsetTop,
// behavior: "smooth" 不能使用,否则无法定位
});
}
return;
}
const emojiElement = hasClosestByClassName(eventTarget, "emojis__item");
if (emojiElement) {
const unicode = emojiElement.getAttribute("data-unicode");
if (this.element.querySelectorAll(".emojis__title").length > 2) {
// /emoji 后会自动添加冒号,导致 range 无法计算,因此不依赖 this.fill
const range = getSelection().getRangeAt(0);
if (range.endContainer.nodeType !== 3) {
range.endContainer.childNodes[range.endOffset - 1]?.remove();
} else if (range.endContainer.textContent === ":") {
// iphone
range.endContainer.textContent = "";
}
addEmoji(unicode);
let emoji;
if (unicode.indexOf(".") > -1) {
emoji = `:${unicode.split(".")[0]}: `;
} else {
emoji = unicode2Emoji(unicode) + " ";
}
insertHTML(protyle.lute.SpinBlockDOM(emoji), protyle, false, true);
this.element.classList.add("fn__none");
} else {
this.fill(unicode, protyle);
}
}
});
}
public render(protyle: IProtyle) {
if (!window.getSelection().focusNode) {
this.element.classList.add("fn__none");
clearTimeout(this.timeId);
return;
}
if (!this.enableExtend) {
clearTimeout(this.timeId);
return;
}
protyle.toolbar.range = getSelection().getRangeAt(0);
// 粘贴后 range.startContainer 为空 https://github.com/siyuan-note/siyuan/issues/7360
if (protyle.toolbar.range.startContainer.nodeType === 3 && protyle.toolbar.range.startContainer.textContent === "") {
const lastSibling = hasPreviousSibling(protyle.toolbar.range.startContainer) as Text;
if (lastSibling && lastSibling.nodeType === 3) {
if (lastSibling.wholeText !== lastSibling.textContent) {
let previousSibling = lastSibling.previousSibling;
while (previousSibling && previousSibling.nodeType === 3) {
if (previousSibling.textContent === "") {
previousSibling = previousSibling.previousSibling;
previousSibling.nextSibling.remove();
} else {
lastSibling.textContent = previousSibling.textContent + lastSibling.textContent;
previousSibling.remove();
break;
}
}
}
protyle.toolbar.range.setStart(lastSibling, lastSibling.textContent.length);
protyle.toolbar.range.collapse(true);
}
}
const start = getSelectionOffset(protyle.toolbar.range.startContainer, protyle.wysiwyg.element).start;
const currentLineValue = protyle.toolbar.range.startContainer.textContent.substring(0, start) || "";
const key = this.getKey(currentLineValue, protyle.options.hint.extend);
if (typeof key === "undefined" ||
hasClosestByAttribute(protyle.toolbar.range.startContainer, "data-type", "code") ||
hasClosestByAttribute(protyle.toolbar.range.startContainer, "data-type", "NodeCodeBlock")) {
this.element.classList.add("fn__none");
clearTimeout(this.timeId);
return;
}
// https://github.com/siyuan-note/siyuan/issues/7933
if (this.splitChar === "#") {
const blockElement = hasClosestBlock(protyle.toolbar.range.startContainer);
if (blockElement && blockElement.getAttribute("data-type") === "NodeHeading") {
const blockIndex = getSelectionOffset(protyle.toolbar.range.startContainer, blockElement).start;
if (blockElement.textContent.startsWith("#".repeat(blockIndex))) {
this.element.classList.add("fn__none");
clearTimeout(this.timeId);
return;
}
}
}
if (this.splitChar === ":") {
clearTimeout(this.timeId);
if (key) {
this.genEmojiHTML(protyle, key);
} else {
this.element.classList.add("fn__none");
}
return;
}
// https://github.com/siyuan-note/siyuan/issues/5083
if (this.splitChar === "/" || this.splitChar === "、") {
clearTimeout(this.timeId);
if (this.enableSlash && !isMobile()) {
this.genHTML(hintSlash(key, protyle), protyle, false, "hint");
}
return;
}
protyle.options.hint.extend.forEach((item) => {
if (item.key === this.splitChar) {
clearTimeout(this.timeId);
this.timeId = window.setTimeout(() => {
this.genHTML(item.hint(key, protyle, "hint"), protyle, false, "hint");
}, protyle.options.hint.delay);
}
});
}
public genLoading(protyle: IProtyle) {
if (this.element.classList.contains("fn__none")) {
this.element.innerHTML = '

';
this.element.classList.remove("fn__none");
const textareaPosition = getSelectionPosition(protyle.wysiwyg.element);
setPosition(this.element, textareaPosition.left, textareaPosition.top + 26, 30);
} else {
this.element.insertAdjacentHTML("beforeend", '
');
}
}
public bindUploadEvent(protyle: IProtyle, element: HTMLElement) {
const uploadElement = element.querySelector('input[type="file"]');
if (uploadElement) {
uploadElement.addEventListener("change", (event: InputEvent & { target: HTMLInputElement }) => {
if (event.target.files.length === 0) {
return;
}
const range = getEditorRange(protyle.wysiwyg.element);
if (this.lastIndex > -1) {
range.setStart(range.startContainer, this.lastIndex);
}
range.deleteContents();
uploadFiles(protyle, event.target.files, event.target);
hideElements(["hint", "toolbar"], protyle);
});
}
}
private getHTMLByData(data: IHintData[]) {
let hintsHTML = '';
if (this.source !== "hint") {
hintsHTML = '
';
}
data.forEach((hintData, i) => {
// https://github.com/siyuan-note/siyuan/issues/1229 提示时,新建文件不应默认选中
let focusClass = "";
if ((i === 1 && data[i].focus) ||
(i === 0 && (data.length === 1 || !data[1].focus))) {
focusClass = " b3-list-item--focus";
}
if (hintData.html === "separator") {
hintsHTML += '';
} else {
hintsHTML += ``;
}
});
return `${hintsHTML}
`;
}
public genHTML(data: IHintData[], protyle: IProtyle, hide = false, source: THintSource) {
this.source = source;
if (data.length === 0) {
if (!this.element.querySelector(".fn__loading") || hide) {
this.element.classList.add("fn__none");
}
return;
}
this.element.innerHTML = this.getHTMLByData(data);
this.element.classList.remove("fn__none");
// https://github.com/siyuan-note/siyuan/issues/4575
if (data[0].filter) {
this.element.classList.add("hint--menu");
} else {
this.element.classList.remove("hint--menu");
}
this.element.style.width = Math.max(protyle.element.clientWidth / 2, 320) + "px";
if (this.source === "av") {
const cellElement = hasClosestByClassName(protyle.toolbar.range.startContainer, "av__cell");
if (cellElement) {
const cellRect = cellElement.getBoundingClientRect();
setPosition(this.element, cellRect.left, cellRect.bottom, cellRect.height);
}
} else {
const textareaPosition = getSelectionPosition(protyle.wysiwyg.element);
setPosition(this.element, textareaPosition.left, textareaPosition.top + 26, 30);
}
this.element.scrollTop = 0;
this.bindUploadEvent(protyle, this.element);
if (this.source !== "hint") {
const searchElement = this.element.querySelector("input.b3-text-field") as HTMLInputElement;
const oldValue = this.element.querySelector("mark")?.textContent || "";
searchElement.value = oldValue;
searchElement.select();
searchElement.addEventListener("keydown", (event: KeyboardEvent) => {
if (event.key !== "Meta" && event.key !== "Control") {
// 需要冒泡以满足光标在块标位置时 ctrl 弹出悬浮层
event.stopPropagation();
}
if (event.isComposing) {
return;
}
upDownHint(this.element.lastElementChild, event);
if (event.key === "Enter") {
setTimeout(() => {
this.fill(decodeURIComponent(this.element.querySelector(".b3-list-item--focus").getAttribute("data-value")), protyle, true, isNotCtrl(event));
}, 148); // 划选引用点击,需先重置 range
focusByRange(protyle.toolbar.range);
event.preventDefault();
} else if (event.key === "Escape") {
this.element.classList.add("fn__none");
focusByRange(protyle.toolbar.range);
}
});
const nodeElement = protyle.toolbar.range ? hasClosestBlock(protyle.toolbar.range.startContainer) : false;
searchElement.addEventListener("input", (event: InputEvent) => {
if (event.isComposing) {
return;
}
event.stopPropagation();
this.genSearchHTML(protyle, searchElement, nodeElement, oldValue);
});
searchElement.addEventListener("compositionend", (event: InputEvent) => {
event.stopPropagation();
this.genSearchHTML(protyle, searchElement, nodeElement, oldValue);
});
}
}
private genSearchHTML(protyle: IProtyle, searchElement: HTMLInputElement, nodeElement: false | HTMLElement, oldValue: string) {
this.element.lastElementChild.innerHTML = '

';
fetchPost("/api/search/searchRefBlock", {
k: searchElement.value,
id: nodeElement ? nodeElement.getAttribute("data-node-id") : protyle.block.parentID,
beforeLen: Math.floor((Math.max(protyle.element.clientWidth / 2, 320) - 58) / 28.8),
rootID: protyle.block.rootID,
}, (response) => {
let searchHTML = "";
if (response.data.newDoc) {
const blockRefText = `((newFile "${oldValue}"${Constants.ZWSP}'${response.data.k}${Lute.Caret}'))`;
searchHTML += `
`;
}
response.data.blocks.forEach((item: IBlock, index: number) => {
const blockRefHTML = `
${oldValue}`;
searchHTML += `
`;
});
if (searchHTML === "") {
searchHTML = `
`;
}
this.element.lastElementChild.innerHTML = searchHTML;
setPosition(this.element, parseInt(this.element.style.left), parseInt(this.element.style.right));
});
}
private genEmojiHTML(protyle: IProtyle, value = "") {
if (value && !this.enableEmoji) {
return;
}
const panelElement = this.element.querySelector(".emojis__panel");
if (panelElement) {
panelElement.innerHTML = filterEmoji(value, 256);
if (value) {
panelElement.nextElementSibling.classList.add("fn__none");
} else {
panelElement.nextElementSibling.classList.remove("fn__none");
}
lazyLoadEmojiImg(panelElement);
} else {
this.element.innerHTML = `
${filterEmoji(value, 256)}
`;
lazyLoadEmoji(this.element);
lazyLoadEmojiImg(this.element);
}
const firstEmojiElement = this.element.querySelector(".emojis__item");
if (firstEmojiElement) {
firstEmojiElement.classList.add("emojis__item--current");
this.element.classList.remove("fn__none");
this.element.style.width = Math.max(protyle.element.clientWidth / 2, 320) + "px";
const textareaPosition = getSelectionPosition(protyle.wysiwyg.element);
setPosition(this.element, textareaPosition.left, textareaPosition.top + 26, 30);
this.element.querySelector(".emojis__panel").scrollTop = 0;
} else {
this.element.classList.add("fn__none");
}
}
public fill(value: string, protyle: IProtyle, updateRange = true, refIsS = false) {
hideElements(["hint", "toolbar"], protyle);
if (updateRange && this.source !== "av") {
protyle.toolbar.range = getEditorRange(protyle.wysiwyg.element);
}
const range = protyle.toolbar.range;
let nodeElement = hasClosestBlock(protyle.toolbar.range.startContainer) as HTMLElement;
if (!nodeElement) {
return;
}
if (this.source === "av") {
const cellElement = hasClosestByClassName(protyle.toolbar.range.startContainer, "av__cell");
if (!cellElement) {
return;
}
const previousID = cellElement.dataset.blockId;
const avID = nodeElement.getAttribute("data-av-id");
let tempElement = document.createElement("div");
tempElement.innerHTML = value.replace(/
/g, "").replace(/<\/mark>/g, "");
tempElement = tempElement.firstElementChild as HTMLDivElement;
if (value.startsWith("((newFile ") && value.endsWith(`${Lute.Caret}'))`)) {
const fileNames = value.substring(11, value.length - 4).split(`"${Constants.ZWSP}'`);
const realFileName = fileNames.length === 1 ? fileNames[0] : fileNames[1];
getSavePath(protyle.path, protyle.notebookId, (pathString) => {
fetchPost("/api/filetree/createDocWithMd", {
notebook: protyle.notebookId,
path: pathPosix().join(pathString, realFileName),
parentID: protyle.block.rootID,
markdown: ""
}, response => {
transaction(protyle, [{
action: "replaceAttrViewBlock",
avID,
previousID,
nextID: response.data,
isDetached: false,
}], [{
action: "replaceAttrViewBlock",
avID,
previousID: response.data,
nextID: previousID,
isDetached: true,
}]);
});
});
} else {
const sourceId = tempElement.getAttribute("data-id");
transaction(protyle, [{
action: "replaceAttrViewBlock",
avID,
previousID,
nextID: sourceId,
isDetached: false,
}], [{
action: "replaceAttrViewBlock",
avID,
previousID: sourceId,
nextID: previousID,
isDetached: true,
}]);
}
return;
}
this.enableExtend = false;
let id = "";
if (nodeElement) {
id = nodeElement.getAttribute("data-node-id");
}
const html = nodeElement.outerHTML;
// 自顶向下法新建文档后光标定位问题 https://github.com/siyuan-note/siyuan/issues/299
// QQ 拼音输入法自动补全需移除补全内容 https://github.com/siyuan-note/siyuan/issues/320
// 前后有标记符的情况 https://github.com/siyuan-note/siyuan/issues/2511
const endSplit = Constants.BLOCK_HINT_CLOSE_KEYS[this.splitChar];
if (Constants.BLOCK_HINT_KEYS.includes(this.splitChar) && endSplit && range.startContainer.nodeType === 3
&& (range.startContainer as Text).wholeText.indexOf(endSplit) > -1
// 在包含 )) 的块中引用时会丢失字符 https://ld246.com/article/1679980200782
&& (range.startContainer as Text).wholeText.indexOf(this.splitChar) > -1) {
let matchEndChar = 0;
let textNode = range.startContainer;
while (textNode && matchEndChar < 2) {
const index = textNode.textContent.indexOf(endSplit);
const startIndex = textNode.textContent.indexOf(this.splitChar);
if (index > -1 && (index < startIndex || startIndex < 0)) {
matchEndChar = 2;
range.setEnd(textNode, index + 2);
break;
}
const indexOne = textNode.textContent.indexOf(endSplit.substr(1));
if (indexOne > -1) {
matchEndChar += 1;
}
if (matchEndChar === 2) {
range.setEnd(textNode, indexOne + 1);
break;
}
textNode = textNode.nextSibling;
}
}
if (this.lastIndex > -1) {
range.setStart(range.startContainer, this.lastIndex);
if (isIPhone()) {
focusByRange(range);
}
}
// 新建文件
if (Constants.BLOCK_HINT_KEYS.includes(this.splitChar) && value.startsWith("((newFile ") && value.endsWith(`${Lute.Caret}'))`)) {
focusByRange(range);
const fileNames = value.substring(11, value.length - 4).split(`"${Constants.ZWSP}'`);
const realFileName = fileNames.length === 1 ? fileNames[0] : fileNames[1];
getSavePath(protyle.path, protyle.notebookId, (pathString) => {
fetchPost("/api/filetree/createDocWithMd", {
notebook: protyle.notebookId,
path: pathPosix().join(pathString, realFileName),
parentID: protyle.block.rootID,
markdown: ""
}, response => {
protyle.toolbar.setInlineMark(protyle, "block-ref", "range", {
type: "id",
color: `${response.data}${Constants.ZWSP}${refIsS ? "s" : "d"}${Constants.ZWSP}${(refIsS ? fileNames[0] : realFileName).substring(0, window.siyuan.config.editor.blockRefDynamicAnchorTextMaxLen)}`
});
});
});
return;
}
if (Constants.BLOCK_HINT_KEYS.includes(this.splitChar)) {
if (value === "") {
const editElement = getContenteditableElement(nodeElement);
if (editElement.textContent === "") {
editElement.innerHTML = "";
focusByWbr(editElement, range);
}
return;
}
let tempElement = document.createElement("div");
tempElement.innerHTML = value.replace(//g, "").replace(/<\/mark>/g, "");
tempElement = tempElement.firstElementChild as HTMLDivElement;
if (refIsS) {
const staticText = range.toString().replace(this.splitChar, "");
if (staticText) {
tempElement.setAttribute("data-subtype", "s");
tempElement.innerText = staticText;
}
} else {
tempElement.setAttribute("data-subtype", "d");
const dynamicTexts = tempElement.innerText.split(Constants.ZWSP);
if (dynamicTexts.length === 2) {
tempElement.innerText = dynamicTexts[1];
}
}
protyle.toolbar.setInlineMark(protyle, "block-ref", "range", {
type: "id",
color: `${tempElement.getAttribute("data-id")}${Constants.ZWSP}${tempElement.getAttribute("data-subtype")}${Constants.ZWSP}${tempElement.textContent}`
});
return;
} else if (this.splitChar === ":") {
addEmoji(value);
let emoji;
if (value.indexOf(".") > -1) {
emoji = `:${value.split(".")[0]}: `;
} else {
emoji = unicode2Emoji(value) + " ";
}
insertHTML(protyle.lute.SpinBlockDOM(emoji), protyle);
} else if (["「「", "「『", "『「", "『『", "{{"].includes(this.splitChar) || this.splitChar === "#" || this.splitChar === ":") {
if (value === "") {
const editElement = getContenteditableElement(nodeElement);
if (editElement.textContent === "") {
editElement.innerHTML = "";
focusByWbr(editElement, range);
}
return;
}
insertHTML(protyle.lute.SpinBlockDOM(value), protyle, false, isMobile());
blockRender(protyle, protyle.wysiwyg.element);
return;
} else if (this.splitChar === "/" || this.splitChar === "、") {
this.enableExtend = true;
if (value === "((" || value === "{{") {
if (value === "((") {
hintRef("", protyle, "hint");
} else {
hintEmbed("", protyle);
}
this.splitChar = value;
this.lastIndex = 0;
range.deleteContents();
const textNode = document.createTextNode(value);
range.insertNode(textNode);
range.setEnd(textNode, value.length);
range.collapse(false);
return;
} else if (value === Constants.ZWSP) {
range.deleteContents();
this.fixImageCursor(range);
protyle.toolbar.showTpl(protyle, nodeElement, range);
updateTransaction(protyle, id, nodeElement.outerHTML, html);
return;
} else if (value === Constants.ZWSP + 1) {
range.deleteContents();
this.fixImageCursor(range);
protyle.toolbar.showWidget(protyle, nodeElement, range);
updateTransaction(protyle, id, nodeElement.outerHTML, html);
return;
} else if (value === Constants.ZWSP + 2) {
range.deleteContents();
this.fixImageCursor(range);
protyle.toolbar.range = range;
const rangePosition = getSelectionPosition(nodeElement, range);
assetMenu(protyle, {x: rangePosition.left, y: rangePosition.top + 26, w: 0, h: 26});
updateTransaction(protyle, id, nodeElement.outerHTML, html);
return;
} else if (value === Constants.ZWSP + 3) {
range.deleteContents();
return;
} else if (value === Constants.ZWSP + 4) {
const newSubDocId = Lute.NewNodeID();
fetchPost("/api/filetree/createDoc", {
notebook: protyle.notebookId,
path: pathPosix().join(getDisplayName(protyle.path, false, true), newSubDocId + ".sy"),
title: "Untitled",
md: ""
}, () => {
insertHTML(`Untitled`, protyle);
/// #if MOBILE
openMobileFileById(protyle.app, newSubDocId, [Constants.CB_GET_HL, Constants.CB_GET_CONTEXT]);
/// #else
openFileById({
app: protyle.app,
id: newSubDocId,
action: [Constants.CB_GET_HL, Constants.CB_GET_CONTEXT]
});
/// #endif
});
return;
} else if (value === Constants.ZWSP + 5) {
range.deleteContents();
AIChat(protyle, nodeElement);
return;
} else if (Constants.INLINE_TYPE.includes(value)) {
range.deleteContents();
focusByRange(range);
if (["a", "block-ref", "inline-math", "inline-memo", "text"].includes(value)) {
protyle.toolbar.element.querySelector(`[data-type="${value}"]`).dispatchEvent(new CustomEvent("click"));
return;
}
protyle.toolbar.setInlineMark(protyle, value, "range");
return;
} else if (value === "emoji") {
range.deleteContents();
range.insertNode(document.createTextNode(":"));
range.collapse(false);
this.genEmojiHTML(protyle);
return;
} else if (value.indexOf("style") > -1) {
range.deleteContents();
this.fixImageCursor(range);
nodeElement.setAttribute("style", value.split(Constants.ZWSP)[1] || "");
updateTransaction(protyle, id, nodeElement.outerHTML, html);
return;
} else if (value.startsWith("plugin")) {
protyle.app.plugins.find((plugin) => {
const ids = value.split(Constants.ZWSP);
if (ids[1] === plugin.name) {
plugin.protyleSlash.find((slash) => {
if (slash.id === ids[2]) {
slash.callback(protyle.getInstance());
return true;
}
});
return true;
}
});
return;
} else {
range.deleteContents();
if (value !== "![]()") {
this.fixImageCursor(range);
}
let textContent = value;
if (value === "```") {
textContent = value + window.siyuan.storage[Constants.LOCAL_CODELANG] + Lute.Caret + "\n```";
}
const editableElement = getContenteditableElement(nodeElement);
if (value === "![]()") { // https://github.com/siyuan-note/siyuan/issues/4586 1
let newHTML = "";
range.insertNode(document.createElement("wbr"));
range.insertNode(document.createTextNode(value));
newHTML = protyle.lute.SpinBlockDOM(nodeElement.outerHTML);
nodeElement.outerHTML = newHTML;
nodeElement = protyle.wysiwyg.element.querySelector(`[data-node-id="${id}"]`);
focusByWbr(nodeElement, range);
updateTransaction(protyle, id, nodeElement.outerHTML, html);
let imgElement: HTMLElement = range.startContainer.childNodes[range.startOffset - 1] as HTMLElement || range.startContainer as HTMLElement;
if (imgElement && imgElement.nodeType !== 3 && imgElement.classList.contains("img")) {
// 已经找到图片
} else if (imgElement.previousSibling?.nodeType !== 3 && (imgElement.previousSibling as HTMLElement).classList.contains("img")) {
// https://github.com/siyuan-note/siyuan/issues/7540
imgElement = imgElement.previousSibling as HTMLElement;
} else {
Array.from(nodeElement.querySelectorAll(".img")).find((item: HTMLElement) => {
if (item.querySelector("img").getAttribute("data-src") === "") {
imgElement = item;
return true;
}
});
}
const rect = imgElement.getBoundingClientRect();
imgMenu(protyle, range, imgElement, {
clientX: rect.left,
clientY: rect.top
});
return;
} else if (editableElement.textContent === "" && nodeElement.getAttribute("data-type") === "NodeParagraph") {
let newHTML = "";
if (value === "") {
newHTML = `
`;
} else {
editableElement.textContent = textContent;
newHTML = protyle.lute.SpinBlockDOM(nodeElement.outerHTML);
}
nodeElement.outerHTML = newHTML;
nodeElement = protyle.wysiwyg.element.querySelector(`[data-node-id="${id}"]`);
// https://github.com/siyuan-note/siyuan/issues/6864
if (nodeElement.getAttribute("data-type") === "NodeTable") {
nodeElement.querySelectorAll("colgroup col").forEach((item: HTMLElement) => {
item.style.minWidth = "60px";
});
newHTML = nodeElement.outerHTML;
}
updateTransaction(protyle, id, newHTML, html);
} else {
let newHTML = protyle.lute.SpinBlockDOM(textContent);
if (value === "
") {
newHTML = `
`;
}
nodeElement.insertAdjacentHTML("afterend", newHTML);
const oldHTML = nodeElement.outerHTML;
const newId = newHTML.substr(newHTML.indexOf('data-node-id="') + 14, 22);
nodeElement = protyle.wysiwyg.element.querySelector(`[data-node-id="${newId}"]`);
// https://github.com/siyuan-note/siyuan/issues/6864
if (nodeElement.getAttribute("data-type") === "NodeTable") {
nodeElement.querySelectorAll("colgroup col").forEach((item: HTMLElement) => {
item.style.minWidth = "60px";
});
}
transaction(protyle, [{
data: oldHTML,
id,
action: "update"
}, {
data: nodeElement.outerHTML,
id: newId,
previousID: id,
action: "insert"
}], [{
id: newId,
action: "delete"
}, {
data: html,
id,
action: "update"
}]);
}
if (value === "
" || value === "$$" || (value.indexOf("```") > -1 && value.length > 3)) {
protyle.toolbar.showRender(protyle, nodeElement);
processRender(nodeElement);
} else if (value.startsWith("```")) {
highlightRender(nodeElement);
} else if (value.startsWith("