Merge branch 'dev' into markmap

This commit is contained in:
Achuan-2 2025-11-25 09:41:27 +08:00 committed by GitHub
commit 95f2c7eec0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
113 changed files with 895 additions and 518 deletions

View file

@ -11,7 +11,7 @@ import {getThemeMode, setInlineStyle} from "../../util/assets";
import {fetchPost, fetchSyncPost} from "../../util/fetch";
import {Dialog} from "../../dialog";
import {replaceLocalPath} from "../../editor/rename";
import {getScreenWidth, isInAndroid, isInHarmony, setStorageVal} from "../util/compatibility";
import {getScreenWidth, isInAndroid, isInHarmony, isInIOS, setStorageVal} from "../util/compatibility";
import {getFrontend} from "../../util/functions";
const getPluginStyle = async () => {
@ -117,13 +117,23 @@ export const saveExport = (option: IExportOptions) => {
const getSnippetCSS = () => {
let snippetCSS = "";
document.querySelectorAll("style").forEach((item) => {
if (item.id.startsWith("snippet")) {
if (item.id.startsWith("snippetCSS")) {
snippetCSS += item.outerHTML;
}
});
return snippetCSS;
};
const getSnippetJS = () => {
let snippetScript = "";
document.querySelectorAll("script").forEach((item) => {
if (item.id.startsWith("snippetJS")) {
snippetScript += item.outerHTML;
}
});
return snippetScript;
};
/// #if !BROWSER
const renderPDF = async (id: string) => {
const localData = window.siyuan.storage[Constants.LOCAL_EXPORTPDF];
@ -624,7 +634,9 @@ ${getIconScript(servePath)}
}
})
});
</script></body></html>`;
</script>
${getSnippetJS()}
</body></html>`;
fetchPost("/api/export/exportTempContent", {content: html}, (response) => {
ipcRenderer.send(Constants.SIYUAN_EXPORT_NEWWINDOW, response.data.url);
});
@ -705,13 +717,14 @@ export const onExport = async (data: IWebSocketData, filePath: string, servePath
themeStyle = `<link rel="stylesheet" type="text/css" id="themeStyle" href="${servePath}appearance/themes/${themeName}/theme.css?${Constants.SIYUAN_VERSION}"/>`;
}
const screenWidth = getScreenWidth();
const mobileHtml = isInAndroid() || isInHarmony() ? {
const isInMobile = isInAndroid() || isInHarmony() || isInIOS();
const mobileHtml = isInMobile ? {
js: `document.body.style.minWidth = "${screenWidth}px";`,
css: `@page { size: A4; margin: 10mm 0 10mm 0; }
.protyle-wysiwyg {padding: 0; margin: 0;}`
} : {js: "", css: ""};
const html = `<!DOCTYPE html>
<html lang="${window.siyuan.config.appearance.lang}" data-theme-mode="${getThemeMode()}" data-light-theme="${window.siyuan.config.appearance.themeLight}" data-dark-theme="${window.siyuan.config.appearance.themeDark}">
<html lang="${window.siyuan.config.appearance.lang}" data-theme-mode="${isInMobile ? "light" : getThemeMode()}" data-light-theme="${window.siyuan.config.appearance.themeLight}" data-dark-theme="${window.siyuan.config.appearance.themeDark}">
<head>
<base href="${servePath}">
<meta charset="utf-8">
@ -775,7 +788,8 @@ ${getIconScript(servePath)}
event.stopPropagation();
})
});
</script></body></html>`;
</script>
${getSnippetJS()}</body></html>`;
// 移动端导出 pdf、浏览器导出 HTML
if (typeof filePath === "undefined") {
return html;

View file

@ -62,7 +62,7 @@ import {openFileById} from "../../editor/util";
import * as path from "path";
/// #endif
import {checkFold} from "../../util/noRelyPCFunction";
import {clearSelect} from "../util/clearSelect";
import {clearSelect} from "../util/clear";
export class Gutter {
public element: HTMLElement;
@ -2102,6 +2102,8 @@ export class Gutter {
this.genClick(nodeElements, protyle, (e: HTMLElement) => {
if (e.classList.contains("av")) {
e.style.justifyContent = "";
} else if (["NodeIFrame", "NodeWidget"].includes(e.getAttribute("data-type"))) {
e.style.margin = "";
} else {
e.style.textAlign = "left";
}
@ -2116,6 +2118,8 @@ export class Gutter {
this.genClick(nodeElements, protyle, (e: HTMLElement) => {
if (e.classList.contains("av")) {
e.style.justifyContent = "center";
} else if (["NodeIFrame", "NodeWidget"].includes(e.getAttribute("data-type"))) {
e.style.margin = "0 auto";
} else {
e.style.textAlign = "center";
}
@ -2130,6 +2134,8 @@ export class Gutter {
this.genClick(nodeElements, protyle, (e: HTMLElement) => {
if (e.classList.contains("av")) {
e.style.justifyContent = "flex-end";
} else if (["NodeIFrame", "NodeWidget"].includes(e.getAttribute("data-type"))) {
e.style.margin = "0 0 0 auto";
} else {
e.style.textAlign = "right";
}
@ -2180,6 +2186,8 @@ export class Gutter {
this.genClick(nodeElements, protyle, (e: HTMLElement) => {
if (e.classList.contains("av")) {
e.style.justifyContent = "";
} else if (["NodeIFrame", "NodeWidget"].includes(e.getAttribute("data-type"))) {
e.style.margin = "";
} else {
e.style.textAlign = "";
e.style.direction = "";

View file

@ -209,8 +209,24 @@ ${unicode2Emoji(emoji.unicode)}</button>`;
if (this.element.classList.contains("fn__none")) {
this.element.innerHTML = '<div class="fn__loading" style="height: 128px;position: initial"><img width="64px" src="/stage/loading-pure.svg"></div>';
this.element.classList.remove("fn__none");
const textareaPosition = getSelectionPosition(protyle.wysiwyg.element);
setPosition(this.element, textareaPosition.left, textareaPosition.top + 26, 30);
if (this.source === "av") {
const cellElement = hasClosestByClassName(protyle.toolbar.range.startContainer, "av__cell");
if (cellElement) {
/// #if !MOBILE
const cellRect = cellElement.getBoundingClientRect();
setPosition(this.element, cellRect.left, cellRect.bottom, cellRect.height);
/// #else
setPosition(this.element, 0, 0);
/// #endif
}
} else {
/// #if !MOBILE
const textareaPosition = getSelectionPosition(protyle.wysiwyg.element);
setPosition(this.element, textareaPosition.left, textareaPosition.top + 26, 30);
/// #else
setPosition(this.element, 0, 0);
/// #endif
}
} else {
this.element.insertAdjacentHTML("beforeend", '<div class="fn__loading"><img width="64px" src="/stage/loading-pure.svg"></div>');
}

View file

@ -347,6 +347,7 @@ export class Protyle {
data: getResponse,
protyle: this.protyle,
action: mergedOptions.action,
scrollPosition: mergedOptions.scrollPosition,
afterCB: () => {
this.afterOnGet(mergedOptions);
}

View file

@ -36,9 +36,10 @@ import {fetchPost, fetchSyncPost} from "../../../util/fetch";
import {scrollCenter} from "../../../util/highlightById";
import {escapeHtml} from "../../../util/escape";
import {editGalleryItem, openGalleryItemMenu} from "./gallery/util";
import {clearSelect} from "../../util/clearSelect";
import {clearSelect} from "../../util/clear";
import {removeCompressURL} from "../../../util/image";
let foldTimeout: number;
export const avClick = (protyle: IProtyle, event: MouseEvent & { target: HTMLElement }) => {
if (isOnlyMeta(event)) {
return false;
@ -228,9 +229,17 @@ export const avClick = (protyle: IProtyle, event: MouseEvent & { target: HTMLEle
event.stopPropagation();
return true;
} else if (type === "av-group-fold") {
if (target.getAttribute("data-folding") !== "true") {
target.setAttribute("data-folding", "true");
const isOpen = target.firstElementChild.classList.contains("av__group-arrow--open");
target.setAttribute("data-processed", "true");
const isOpen = target.firstElementChild.classList.contains("av__group-arrow--open");
if (isOpen) {
target.firstElementChild.classList.remove("av__group-arrow--open");
target.parentElement.nextElementSibling.classList.add("fn__none");
} else {
target.firstElementChild.classList.add("av__group-arrow--open");
target.parentElement.nextElementSibling.classList.remove("fn__none");
}
clearTimeout(foldTimeout);
foldTimeout = window.setTimeout(() => {
transaction(protyle, [{
action: "foldAttrViewGroup",
avID: blockElement.dataset.avId,
@ -244,14 +253,7 @@ export const avClick = (protyle: IProtyle, event: MouseEvent & { target: HTMLEle
id: target.dataset.id,
data: !isOpen
}]);
if (isOpen) {
target.firstElementChild.classList.remove("av__group-arrow--open");
target.parentElement.nextElementSibling.classList.add("fn__none");
} else {
target.firstElementChild.classList.add("av__group-arrow--open");
target.parentElement.nextElementSibling.classList.remove("fn__none");
}
}
}, Constants.TIMEOUT_COUNT);
event.preventDefault();
event.stopPropagation();
return true;

View file

@ -2,7 +2,7 @@ import {genCellValue, getTypeByCellElement, renderCell, renderCellAttr} from "..
import {fetchPost} from "../../../../util/fetch";
import {setPage} from "../row";
import {Constants} from "../../../../constants";
import {clearSelect} from "../../../util/clearSelect";
import {clearSelect} from "../../../util/clear";
export const insertGalleryItemAnimation = (options: {
blockElement: HTMLElement;

View file

@ -104,8 +104,8 @@ ${cell.color ? `color:${cell.color};` : ""}">${renderCell(cell.value, rowIndex,
});
galleryHTML += `</div>
<div class="av__gallery-actions">
<span class="protyle-icon protyle-icon--first b3-tooltips b3-tooltips__n" aria-label="${window.siyuan.languages.displayEmptyFields}" data-type="av-gallery-edit"><svg><use xlink:href="#iconEdit"></use></svg></span>
<span class="protyle-icon protyle-icon--last b3-tooltips b3-tooltips__n" aria-label="${window.siyuan.languages.more}" data-type="av-gallery-more"><svg><use xlink:href="#iconMore"></use></svg></span>
<span class="protyle-icon protyle-icon--first ariaLabel" data-position="4north" aria-label="${window.siyuan.languages.displayEmptyFields}" data-type="av-gallery-edit"><svg><use xlink:href="#iconEdit"></use></svg></span>
<span class="protyle-icon protyle-icon--last ariaLabel" data-position="4north" aria-label="${window.siyuan.languages.more}" data-type="av-gallery-more"><svg><use xlink:href="#iconMore"></use></svg></span>
</div>
</div>`;
});

View file

@ -29,7 +29,7 @@ const getKanbanTitleHTML = (group: IAVView, counter: number) => {
// av__group-name 为第三方需求,本应用内没有使用,但不能移除 https://github.com/siyuan-note/siyuan/issues/15736
return `<div class="av__group-title">
<span class="av__group-name fn__ellipsis" style="white-space: nowrap;">${nameHTML}</span>
${counter === 0 ? '<span class="fn__space"></span>' : `<span aria-label="${window.siyuan.languages.entryNum}" data-position="north" class="av__group-counter ariaLabel">${counter}</span>`}
${(!counter || counter === 0) ? '<span class="fn__space"></span>' : `<span aria-label="${window.siyuan.languages.entryNum}" data-position="north" class="av__group-counter ariaLabel">${counter}</span>`}
<span class="fn__flex-1"></span>
<span class="av__group-icon av__group-icon--hover ariaLabel" data-type="av-add-top" data-position="north" aria-label="${window.siyuan.languages.newRow}"><svg><use xlink:href="#iconAdd"></use></svg></span>
</div>`;
@ -100,8 +100,8 @@ ${cell.color ? `color:${cell.color};` : ""}">${renderCell(cell.value, rowIndex,
});
galleryHTML += `</div>
<div class="av__gallery-actions">
<span class="protyle-icon protyle-icon--first b3-tooltips b3-tooltips__n" aria-label="${window.siyuan.languages.displayEmptyFields}" data-type="av-gallery-edit"><svg><use xlink:href="#iconEdit"></use></svg></span>
<span class="protyle-icon protyle-icon--last b3-tooltips b3-tooltips__n" aria-label="${window.siyuan.languages.more}" data-type="av-gallery-more"><svg><use xlink:href="#iconMore"></use></svg></span>
<span class="protyle-icon protyle-icon--first ariaLabel" data-position="4north" aria-label="${window.siyuan.languages.displayEmptyFields}" data-type="av-gallery-edit"><svg><use xlink:href="#iconEdit"></use></svg></span>
<span class="protyle-icon protyle-icon--last ariaLabel" data-position="4north" aria-label="${window.siyuan.languages.more}" data-type="av-gallery-more"><svg><use xlink:href="#iconMore"></use></svg></span>
</div>
</div>`;
});

View file

@ -5,7 +5,7 @@ import {avContextmenu} from "./action";
import {hasClosestByClassName} from "../../util/hasClosest";
import {Constants} from "../../../constants";
import {upDownHint} from "../../../util/upDownHint";
import {clearSelect} from "../../util/clearSelect";
import {clearSelect} from "../../util/clear";
export const avKeydown = (event: KeyboardEvent, nodeElement: HTMLElement, protyle: IProtyle) => {
if (!nodeElement.classList.contains("av") || !window.siyuan.menus.menu.element.classList.contains("fn__none")) {

View file

@ -218,7 +218,7 @@ export const bindLayoutEvent = (options: {
return;
}
const toggleBgElement = options.menuElement.querySelector('.b3-switch[data-type="toggle-kanban-bg"]') as HTMLInputElement;
toggleBgElement.addEventListener("change", () => {
toggleBgElement?.addEventListener("change", () => {
const checked = toggleBgElement.checked;
transaction(options.protyle, [{
action: "setAttrViewFillColBackgroundColor",

View file

@ -17,7 +17,7 @@ import {renderGallery} from "./gallery/render";
import {getFieldsByData, getViewIcon} from "./view";
import {openMenuPanel} from "./openMenuPanel";
import {getPageSize} from "./groups";
import {clearSelect} from "../../util/clearSelect";
import {clearSelect} from "../../util/clear";
import {showMessage} from "../../../dialog/message";
import {renderKanban} from "./kanban/render";
@ -251,7 +251,7 @@ export const getGroupTitleHTML = (group: IAVView, counter: number) => {
</div>
<span class="fn__space"></span>
<span class="av__group-name">${nameHTML}</span>
${counter === 0 ? '<span class="fn__space"></span>' : `<span aria-label="${window.siyuan.languages.entryNum}" data-position="north" class="av__group-counter ariaLabel">${counter}</span>`}
${(!counter || counter === 0) ? '<span class="fn__space"></span>' : `<span aria-label="${window.siyuan.languages.entryNum}" data-position="north" class="av__group-counter ariaLabel">${counter}</span>`}
<span class="av__group-icon av__group-icon--hover ariaLabel" data-type="av-add-top" data-position="north" aria-label="${window.siyuan.languages.newRow}"><svg><use xlink:href="#iconAdd"></use></svg></span>
</div>`;
};
@ -784,6 +784,10 @@ export const refreshAV = (protyle: IProtyle, operation: IOperation) => {
getAVElements(protyle, operation.avID).forEach((item) => {
const foldElement = item.querySelector(`[data-type="av-group-fold"][data-id="${operation.id}"]`);
if (foldElement) {
if (foldElement.getAttribute("data-processed") === "true") {
foldElement.removeAttribute("data-processed");
return;
}
if (operation.data) {
foldElement.firstElementChild.classList.remove("av__group-arrow--open");
foldElement.parentElement.nextElementSibling.classList.add("fn__none");

View file

@ -7,7 +7,7 @@ import {fetchPost} from "../../../util/fetch";
import * as dayjs from "dayjs";
import {Constants} from "../../../constants";
import {insertGalleryItemAnimation} from "./gallery/item";
import {clearSelect} from "../../util/clearSelect";
import {clearSelect} from "../../util/clear";
import {isCustomAttr} from "./blockAttr";
export const getFieldIdByCellElement = (cellElement: Element, viewType: TAVView): string => {

View file

@ -12,7 +12,7 @@ export const genIconHTML = (element?: false | HTMLElement, actions = ["edit", "m
}
}
const mapActionToHTML = (action: string, isFirst: boolean, isLast: boolean) => {
const classList = ["b3-tooltips__nw", "b3-tooltips", "protyle-icon"];
const classList = ["ariaLabel", "protyle-icon"];
if (isFirst) classList.push("protyle-icon--first");
if (isLast) classList.push("protyle-icon--last");
let aria = "";
@ -45,7 +45,7 @@ export const genIconHTML = (element?: false | HTMLElement, actions = ["edit", "m
}
// Only the edit button honors read-only enable
const hidden = (action === "edit" && !enable) ? " fn__none" : "";
return `<span aria-label="${aria}" class="${classList.join(" ")} ${className}${hidden}"><svg><use xlink:href="#${icon}"></use></svg></span>`;
return `<span aria-label="${aria}" data-position="4north" class="${classList.join(" ")} ${className}${hidden}"><svg><use xlink:href="#${icon}"></use></svg></span>`;
};
const res: string[] = [];
for (let i = 0; i < actions.length; i++) {
@ -65,9 +65,9 @@ export const genRenderFrame = (renderElement: Element) => {
const type = renderElement.getAttribute("data-type");
if (type === "NodeBlockQueryEmbed") {
renderElement.insertAdjacentHTML("afterbegin", `<div class="protyle-icons${isInEmbedBlock(renderElement) ? " fn__none" : ""}">
<span aria-label="${window.siyuan.languages.refresh}" class="b3-tooltips__nw b3-tooltips protyle-icon protyle-action__reload protyle-icon--first"><svg class="fn__rotate"><use xlink:href="#iconRefresh"></use></svg></span>
<span aria-label="${window.siyuan.languages.update} SQL" class="b3-tooltips__nw b3-tooltips protyle-icon protyle-action__edit"><svg><use xlink:href="#iconEdit"></use></svg></span>
<span aria-label="${window.siyuan.languages.more}" class="b3-tooltips__nw b3-tooltips protyle-icon protyle-action__menu protyle-icon--last"><svg><use xlink:href="#iconMore"></use></svg></span>
<span aria-label="${window.siyuan.languages.refresh}" data-position="4north" class="ariaLabel protyle-icon protyle-action__reload protyle-icon--first"><svg class="fn__rotate"><use xlink:href="#iconRefresh"></use></svg></span>
<span aria-label="${window.siyuan.languages.update} SQL" data-position="4north" class="ariaLabel protyle-icon protyle-action__edit"><svg><use xlink:href="#iconEdit"></use></svg></span>
<span aria-label="${window.siyuan.languages.more}" data-position="4north" class="ariaLabel protyle-icon protyle-action__menu protyle-icon--last"><svg><use xlink:href="#iconMore"></use></svg></span>
</div><div class="protyle-cursor">${Constants.ZWSP}</div>`);
} else if (type === "NodeMathBlock" || renderElement.getAttribute("data-subtype") === "math") {
renderElement.firstElementChild.innerHTML = `<span></span><span class="protyle-cursor">${Constants.ZWSP}</span>`;

View file

@ -216,7 +216,7 @@ export const toolbarKeyToMenu = (toolbar: Array<string | IMenuItem>) => {
};
export const copyTextByType = async (ids: string[],
type: "ref" | "blockEmbed" | "protocol" | "protocolMd" | "hPath" | "id") => {
type: "ref" | "blockEmbed" | "protocol" | "protocolMd" | "hPath" | "id" | "webURL") => {
let text = "";
for (let i = 0; i < ids.length; i++) {
const id = ids[i];
@ -236,6 +236,8 @@ export const copyTextByType = async (ids: string[],
} else if (type === "hPath") {
const response = await fetchSyncPost("/api/filetree/getHPathByID", {id});
text += response.data;
} else if (type === "webURL") {
text += `${window.location.origin}?id=${id}`;
} else if (type === "id") {
text += id;
}

View file

@ -1,4 +1,19 @@
import {updateHeader} from "../render/av/row";
import {Constants} from "../../constants";
export const clearBlockElement = (element: Element) => {
element.classList.remove("protyle-wysiwyg--select", "protyle-wysiwyg--hl");
element.removeAttribute(Constants.CUSTOM_RIFF_DECKS);
element.removeAttribute("refcount");
element.querySelector(".protyle-attr--av")?.remove();
element.querySelector(".protyle-attr--refcount")?.remove();
element.removeAttribute("custom-avs");
element.getAttributeNames().forEach(attr => {
if (attr.startsWith("custom-sy-av-s-text-")) {
element.removeAttribute(attr);
}
});
};
export const clearSelect = (types: ("av" | "img" | "cell" | "row" | "galleryItem")[], element: Element) => {
if (types.includes("cell")) {

View file

@ -34,7 +34,7 @@ import {webUtils} from "electron";
import {addDragFill, getTypeByCellElement} from "../render/av/cell";
import {processClonePHElement} from "../render/util";
import {insertGalleryItemAnimation} from "../render/av/gallery/item";
import {clearSelect} from "./clearSelect";
import {clearSelect} from "./clear";
import {dragoverTab} from "../render/av/view";
// position: afterbegin 为拖拽成超级块; "afterend", "beforebegin" 一般拖拽

View file

@ -26,7 +26,8 @@ export const onGet = (options: {
protyle: IProtyle,
action?: TProtyleAction[],
scrollAttr?: IScrollAttr
updateReadonly?: boolean
updateReadonly?: boolean,
scrollPosition?: ScrollLogicalPosition,
afterCB?: () => void
}) => {
if (!options.action) {
@ -96,6 +97,7 @@ export const onGet = (options: {
updateReadonly: options.updateReadonly,
isSyncing: options.data.data.isSyncing,
afterCB: options.afterCB,
scrollPosition: options.scrollPosition
}, options.protyle);
removeLoading(options.protyle);
return;
@ -122,6 +124,7 @@ export const onGet = (options: {
updateReadonly: options.updateReadonly,
isSyncing: options.data.data.isSyncing,
afterCB: options.afterCB,
scrollPosition: options.scrollPosition
}, options.protyle);
removeLoading(options.protyle);
});
@ -133,7 +136,8 @@ const setHTML = (options: {
isSyncing: boolean,
expand: boolean,
updateReadonly?: boolean,
scrollAttr?: IScrollAttr
scrollAttr?: IScrollAttr,
scrollPosition?: ScrollLogicalPosition,
afterCB?: () => void
}, protyle: IProtyle) => {
if (protyle.contentElement.classList.contains("fn__none") && protyle.wysiwyg.element.innerHTML !== "") {
@ -256,7 +260,7 @@ const setHTML = (options: {
}
}
focusElementById(protyle, options.action, options.scrollAttr);
focusElementById(protyle, options.action, options.scrollAttr, options.scrollPosition);
if (options.action.includes(Constants.CB_GET_SETID)) {
// 点击大纲后,如果需要动态加载,在定位后,需要重置 block.id https://github.com/siyuan-note/siyuan/issues/4487
@ -448,7 +452,7 @@ export const enableProtyle = (protyle: IProtyle) => {
hideTooltip();
};
const focusElementById = (protyle: IProtyle, action: string[], scrollAttr?: IScrollAttr) => {
const focusElementById = (protyle: IProtyle, action: string[], scrollAttr?: IScrollAttr, scrollPosition?: ScrollLogicalPosition) => {
let focusElement: Element;
if (scrollAttr && scrollAttr.focusId) {
focusElement = protyle.wysiwyg.element.querySelector(`[data-node-id="${scrollAttr.focusId}"]`);
@ -460,6 +464,9 @@ const focusElementById = (protyle: IProtyle, action: string[], scrollAttr?: IScr
}
});
}
if (!focusElement && protyle.block.id === protyle.block.rootID) {
focusElement = protyle.title.editElement;
}
if (protyle.block.mode === 4) {
preventScroll(protyle);
focusElement = protyle.wysiwyg.element.lastElementChild;
@ -491,7 +498,7 @@ const focusElementById = (protyle: IProtyle, action: string[], scrollAttr?: IScr
protyle.observerLoad?.disconnect();
if (action.includes(Constants.CB_GET_FOCUS) || action.includes(Constants.CB_GET_SCROLL) || action.includes(Constants.CB_GET_HL) || action.includes(Constants.CB_GET_FOCUSFIRST)) {
if (!hasScrollTop) {
scrollCenter(protyle, focusElement);
scrollCenter(protyle, focusElement, scrollPosition);
}
} else {
return;
@ -503,7 +510,7 @@ const focusElementById = (protyle: IProtyle, action: string[], scrollAttr?: IScr
}
if (action.includes(Constants.CB_GET_FOCUS) || action.includes(Constants.CB_GET_HL) || action.includes(Constants.CB_GET_FOCUSFIRST)) {
if (!hasScrollTop) {
scrollCenter(protyle, focusElement);
scrollCenter(protyle, focusElement, scrollPosition);
}
}
});

View file

@ -14,6 +14,7 @@ import {hideElements} from "../ui/hideElements";
import {avRender} from "../render/av/render";
import {cellScrollIntoView, getCellText} from "../render/av/cell";
import {getContenteditableElement} from "../wysiwyg/getBlock";
import {clearBlockElement} from "./clear";
export const getTextStar = (blockElement: HTMLElement) => {
const dataType = blockElement.dataset.type;
@ -407,10 +408,7 @@ export const paste = async (protyle: IProtyle, event: (ClipboardEvent | DragEven
tempElement.querySelectorAll("[data-node-id]").forEach((e) => {
const newId = Lute.NewNodeID();
e.setAttribute("data-node-id", newId);
e.removeAttribute(Constants.CUSTOM_RIFF_DECKS);
e.classList.remove("protyle-wysiwyg--select", "protyle-wysiwyg--hl");
e.setAttribute("updated", newId.split("-")[0]);
e.removeAttribute("refcount");
clearBlockElement(e);
isBlock = true;
});
if (nodeElement.classList.contains("table")) {

View file

@ -15,6 +15,7 @@ import {processClonePHElement} from "../render/util";
import {copyTextByType} from "../toolbar/util";
import {hasClosestByTag, hasTopClosestByClassName} from "../util/hasClosest";
import {removeEmbed} from "./removeEmbed";
import {clearBlockElement} from "../util/clear";
export const commonHotkey = (protyle: IProtyle, event: KeyboardEvent, nodeElement?: HTMLElement) => {
if (matchHotKey(window.siyuan.config.keymap.editor.general.netImg2LocalAsset.custom, event)) {
@ -313,19 +314,14 @@ export const duplicateBlock = async (nodeElements: Element[], protyle: IProtyle)
focusElement = tempElement;
}
tempElement.setAttribute("data-node-id", newId);
tempElement.removeAttribute(Constants.CUSTOM_RIFF_DECKS);
tempElement.classList.remove("protyle-wysiwyg--hl");
tempElement.setAttribute("updated", newId.split("-")[0]);
tempElement.removeAttribute("refcount");
tempElement.lastElementChild.querySelector(".protyle-attr--refcount")?.remove();
clearBlockElement(tempElement);
tempElement.classList.add("protyle-wysiwyg--select");
tempElement.querySelectorAll("[data-node-id]").forEach(childItem => {
const subNewId = Lute.NewNodeID();
childItem.setAttribute("data-node-id", subNewId);
childItem.removeAttribute(Constants.CUSTOM_RIFF_DECKS);
childItem.classList.remove("protyle-wysiwyg--hl");
childItem.setAttribute("updated", subNewId.split("-")[0]);
childItem.removeAttribute("refcount");
childItem.lastElementChild.querySelector(".protyle-attr--refcount")?.remove();
clearBlockElement(childItem);
});
if (typeof starIndex === "number") {
const orderIndex = starIndex + index + 1;
@ -354,13 +350,11 @@ export const duplicateBlock = async (nodeElements: Element[], protyle: IProtyle)
}
childItem.querySelectorAll("[data-node-id]").forEach(subItem => {
subItem.setAttribute("data-node-id", Lute.NewNodeID());
subItem.removeAttribute(Constants.CUSTOM_RIFF_DECKS);
subItem.removeAttribute("refcount");
clearBlockElement(subItem);
});
const newChildId = Lute.NewNodeID();
childItem.setAttribute("data-node-id", newChildId);
childItem.removeAttribute(Constants.CUSTOM_RIFF_DECKS);
childItem.removeAttribute("refcount");
clearBlockElement(childItem);
doOperations.push({
context: {
ignoreProcess: "true"

View file

@ -101,6 +101,16 @@ import { hideTooltip } from "../../dialog/tooltip";
import { openGalleryItemMenu } from "../render/av/gallery/util";
import { clearSelect } from "../util/clearSelect";
import { chartRender } from "../render/chartRender";
import {openEmojiPanel, unicode2Emoji} from "../../emoji";
import {openLink} from "../../editor/openLink";
import {mathRender} from "../render/mathRender";
import {editAssetItem} from "../render/av/asset";
import {img3115} from "../../boot/compatibleVersion";
import {globalClickHideMenu} from "../../boot/globalEvent/click";
import {hideTooltip} from "../../dialog/tooltip";
import {openGalleryItemMenu} from "../render/av/gallery/util";
import {clearSelect} from "../util/clear";
import {chartRender} from "../render/chartRender";
export class WYSIWYG {
public lastHTMLs: { [key: string]: string } = {};
@ -152,7 +162,7 @@ export class WYSIWYG {
}
}
ialKeys.forEach((key: string) => {
if (!["title-img", "title", "updated", "icon", "id", "type", "class", "spellcheck", "contenteditable", "data-doc-type", "style", "data-realwidth", "data-readonly"].includes(key)) {
if (!["title-img", "title", "updated", "icon", "id", "type", "class", "spellcheck", "contenteditable", "data-doc-type", "style", "data-realwidth", "data-readonly", "av-names"].includes(key)) {
this.element.setAttribute(key, ial[key]);
}
});
@ -655,8 +665,13 @@ export class WYSIWYG {
}
if (isOnlyMeta(event) && !event.shiftKey && !event.altKey) {
let ctrlElement = nodeElement;
if (!hasSelectClassElement && galleryItemElement) {
galleryItemElement.classList.toggle("av__gallery-item--select");
const rowElement = hasClosestByClassName(target, "av__row");
if (!hasSelectClassElement && (galleryItemElement || (rowElement && !rowElement.classList.contains("av__row--header")))) {
if (galleryItemElement) {
galleryItemElement.classList.toggle("av__gallery-item--select");
} else if (rowElement) {
selectRow(rowElement.querySelector(".av__firstcol"), "toggle");
}
} else if (ctrlElement) {
const embedBlockElement = isInEmbedBlock(ctrlElement);
if (embedBlockElement) {
@ -917,15 +932,20 @@ export class WYSIWYG {
};
return false;
}
// 图片、iframe、video 缩放
// 图片、iframe、video、挂件缩放
if (!protyle.disabled && target.classList.contains("protyle-action__drag")) {
if (!nodeElement) {
return;
}
let isCenter = true;
if (["NodeIFrame", "NodeWidget", "NodeVideo"].includes(nodeElement.getAttribute("data-type"))) {
if ("NodeVideo" === nodeElement.dataset.type) {
nodeElement.classList.add("iframe--drag");
if (nodeElement.style.textAlign === "left" || nodeElement.style.textAlign === "right") {
if (["left", "right", ""].includes(nodeElement.style.textAlign)) {
isCenter = false;
}
} else if (["NodeIFrame", "NodeWidget"].includes(nodeElement.dataset.type)) {
nodeElement.classList.add("iframe--drag");
if (!nodeElement.style.margin) {
isCenter = false;
}
} else if (target.parentElement.parentElement.getAttribute("data-type") === "img") {
@ -943,6 +963,11 @@ export class WYSIWYG {
if (dragElement.tagName === "IMG") {
img3115(imgElement);
}
// 3.4.1 以前历史数据兼容
if (dragElement.tagName === "IFRAME") {
dragElement.style.height = "";
dragElement.style.width = "";
}
documentSelf.onmousemove = (moveEvent: MouseEvent) => {
if (dragElement.tagName === "IMG") {
dragElement.style.height = "";
@ -951,13 +976,19 @@ export class WYSIWYG {
const multiple = ((dragElement.tagName === "IMG" && !imgElement.style.minWidth && nodeElement.style.textAlign !== "center") || !isCenter) ? 1 : 2;
if (dragElement.tagName === "IMG") {
dragElement.parentElement.style.width = Math.max(17, dragWidth + (moveEvent.clientX - x) * multiple) + "px";
} else if (dragElement.tagName === "IFRAME") {
nodeElement.style.width = Math.max(17, dragWidth + (moveEvent.clientX - x) * multiple) + "px";
} else {
dragElement.style.width = Math.max(17, dragWidth + (moveEvent.clientX - x) * multiple) + "px";
}
}
if (dragElement.tagName !== "IMG") {
if (moveEvent.clientY > y - dragHeight + 8 && moveEvent.clientY < mostBottom) {
dragElement.style.height = (dragHeight + (moveEvent.clientY - y)) + "px";
if (dragElement.tagName === "IFRAME") {
nodeElement.style.height = (dragHeight + (moveEvent.clientY - y)) + "px";
} else {
dragElement.style.height = (dragHeight + (moveEvent.clientY - y)) + "px";
}
}
}
};

View file

@ -763,6 +763,16 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => {
}
}
}
if (selectText === "" && event.key === "ArrowLeft" && position.start === 1 &&
range.startContainer.textContent === Constants.ZWSP) {
range.setStart(range.startContainer, 0);
range.collapse(true);
}
if (selectText === "" && event.key === "ArrowRight" && position.start === 0 &&
range.startContainer.textContent === Constants.ZWSP) {
range.setStart(range.startContainer, 1);
range.collapse(true);
}
return;
}

View file

@ -276,7 +276,7 @@ export const breakList = (protyle: IProtyle, blockElement: Element, range: Range
action: "delete"
});
Array.from(listItemElement.children).reverse().forEach((item) => {
Array.from(listItemElement.children).reverse().forEach((item, index) => {
if (!item.classList.contains("protyle-action") && !item.classList.contains("protyle-attr")) {
doOperations.push({
id: item.getAttribute("data-node-id"),
@ -286,7 +286,8 @@ export const breakList = (protyle: IProtyle, blockElement: Element, range: Range
undoOperations.push({
id: item.getAttribute("data-node-id"),
action: "move",
parentID: listItemId
parentID: listItemId,
data: index === listItemElement.childElementCount - 2 ? "focus" : null
});
listItemElement.parentElement.after(item);
}

View file

@ -611,13 +611,14 @@ export const onTransaction = (protyle: IProtyle, operation: IOperation, isUndo:
data.new.style += ";animation:addCard 450ms linear";
}
Object.keys(data.new).forEach(key => {
if ("id" === key) {
if ("id" === key || "av-names" === key) {
// 设置属性以后不应该给块元素添加 id 属性 No longer add the `id` attribute to block elements after setting the attribute https://github.com/siyuan-note/siyuan/issues/15327
// av-names 属性仅用于生成角标,不添加到元素
return;
}
item.setAttribute(key, data.new[key]);
if (key === Constants.CUSTOM_RIFF_DECKS && data.new[Constants.CUSTOM_RIFF_DECKS] !== data.old[Constants.CUSTOM_RIFF_DECKS]) {
if (key === Constants.CUSTOM_RIFF_DECKS && key !== data.old[Constants.CUSTOM_RIFF_DECKS]) {
item.style.animation = "addCard 450ms linear";
setTimeout(() => {
if (item.parentElement) {