diff --git a/app/appearance/langs/es_ES.json b/app/appearance/langs/es_ES.json
index 06c61d6520f..933603eb647 100644
--- a/app/appearance/langs/es_ES.json
+++ b/app/appearance/langs/es_ES.json
@@ -1063,7 +1063,7 @@
"export20": "La exportación de archivos Word .docx requiere la conversión del formato mediante Pandoc",
"export21": "Exportar plantilla de pie de página PDF",
"export22": "%page
es el número de página actual, %pages
es el número de página total y es compatible con las funciones de plantilla de Sprig ",
- "export23": "Exportar descuento con YAML front-matter",
+ "export23": "Exportar Markdown con YAML front-matter",
"export24": "Después de habilitar, agregue información general de metadatos al comienzo del archivo Markdown exportado",
"export25": "Exportar ruta de plantilla .docx de Word",
"export26": "La ruta absoluta de la plantilla utilizada al exportar archivos .docx de Word, es decir, Pandoc --reference-doc valor del parámetro",
diff --git a/app/src/assets/scss/business/_drag.scss b/app/src/assets/scss/business/_drag.scss
index 75e3df422fb..17424611479 100644
--- a/app/src/assets/scss/business/_drag.scss
+++ b/app/src/assets/scss/business/_drag.scss
@@ -1,5 +1,5 @@
.dragover {
- background-color: var(--b3-theme-primary-lightest);
+ background-color: var(--b3-theme-primary-lightest) !important;
// 需要 !important,否则拖拽到闪卡无效果
&__top {
diff --git a/app/src/assets/scss/protyle/_wysiwyg.scss b/app/src/assets/scss/protyle/_wysiwyg.scss
index de8b02d9467..dd52c91bc77 100644
--- a/app/src/assets/scss/protyle/_wysiwyg.scss
+++ b/app/src/assets/scss/protyle/_wysiwyg.scss
@@ -55,6 +55,18 @@
}
}
+ .bq {
+ .dragover {
+ &__top:not(.av__row) {
+ box-shadow: 0 -3px 0 var(--b3-theme-primary-lighter), inset 0 1px 0 var(--b3-theme-primary-lighter) !important;
+ }
+
+ &__bottom:not(.av__row) {
+ box-shadow: 0 3px 0 var(--b3-theme-primary-lighter), inset 0 -1px 0 var(--b3-theme-primary-lighter) !important;
+ }
+ }
+ }
+
&.list {
padding-left: 0;
@@ -449,6 +461,10 @@
&--select {
background-color: var(--b3-theme-primary-lightest) !important;
+
+ [data-node-id][style*="background-color"] {
+ opacity: .86;
+ }
}
// https://github.com/siyuan-note/siyuan/issues/11589
diff --git a/app/src/block/Panel.ts b/app/src/block/Panel.ts
index 49f996ae63b..ec10d0f3275 100644
--- a/app/src/block/Panel.ts
+++ b/app/src/block/Panel.ts
@@ -124,7 +124,7 @@ export class BlockPanel {
openFileById({
app: options.app,
id: this.nodeIds[0],
- action: this.editors[0].protyle.block.rootID !== this.nodeIds[0] ? [Constants.CB_GET_ALL] : [Constants.CB_GET_CONTEXT],
+ action: this.editors[0].protyle.block.rootID !== this.nodeIds[0] ? [Constants.CB_GET_ALL, Constants.CB_GET_FOCUS] : [Constants.CB_GET_CONTEXT],
});
/// #endif
}
@@ -237,7 +237,7 @@ export class BlockPanel {
let openHTML = "";
/// #if !BROWSER
if (this.nodeIds.length === 1) {
- openHTML = `
+ openHTML = `
`;
diff --git a/app/src/config/keymap.ts b/app/src/config/keymap.ts
index 46fbc7b49c2..01f0cba4256 100644
--- a/app/src/config/keymap.ts
+++ b/app/src/config/keymap.ts
@@ -230,7 +230,8 @@ export const keymap = {
});
},
search(value: string, keymapString: string) {
- keymap.element.querySelectorAll("#keymapList .b3-list-item--hide-action > .b3-list-item__text").forEach(item => {
+ const keymapListElement = keymap.element.querySelector("#keymapList")
+ keymapListElement.querySelectorAll(".b3-list-item--hide-action > .b3-list-item__text").forEach(item => {
const liElement = item.parentElement;
let matchedKeymap = false;
if (keymapString === "" || liElement.querySelector(".b3-text-field").getAttribute("data-value").indexOf(keymapString) > -1) {
@@ -245,10 +246,9 @@ export const keymap = {
}
if (!liElement.nextElementSibling) {
const toggleElement = liElement.parentElement.previousElementSibling;
- const toggleIconElement = toggleElement.querySelector(".b3-list-item__arrow");
if (value === "" && keymapString === "") {
// 复原折叠状态
- if (toggleIconElement.classList.contains("b3-list-item__arrow--open")) {
+ if (toggleElement.querySelector(".b3-list-item__arrow").classList.contains("b3-list-item__arrow--open")) {
liElement.parentElement.classList.remove("fn__none");
} else {
liElement.parentElement.classList.add("fn__none");
@@ -262,8 +262,14 @@ export const keymap = {
}
}
});
- // 编辑器中三级菜单单独处理
- const editorKeymapElement = keymap.element.querySelector("#keymapList").lastElementChild;
+ // 编辑器单独处理
+ this._toggleSearchItem(keymapListElement.lastElementChild, value, keymapString);
+ // 插件单独处理
+ if (keymapListElement.childElementCount === 5) {
+ this._toggleSearchItem(keymapListElement.lastElementChild.previousElementSibling, value, keymapString);
+ }
+ },
+ _toggleSearchItem(editorKeymapElement: HTMLElement, value: string, keymapString: string) {
if (value === "" && keymapString === "") {
// 复原折叠状态
if (editorKeymapElement.querySelector(".b3-list-item__arrow").classList.contains("b3-list-item__arrow--open")) {
diff --git a/app/src/dialog/processSystem.ts b/app/src/dialog/processSystem.ts
index d78a5dad3ac..b371a0cc3c3 100644
--- a/app/src/dialog/processSystem.ts
+++ b/app/src/dialog/processSystem.ts
@@ -201,6 +201,11 @@ export const setDefRefCount = (data: {
attrElement.innerHTML = `
${data.refCount}
${Constants.ZWSP}`;
}
}
+ if (data.refCount === 0) {
+ item.removeAttribute("refcount");
+ } else {
+ item.setAttribute("refcount", data.refCount.toString());
+ }
});
});
diff --git a/app/src/layout/dock/Files.ts b/app/src/layout/dock/Files.ts
index 2e9b0be90d6..0fb2d656d36 100644
--- a/app/src/layout/dock/Files.ts
+++ b/app/src/layout/dock/Files.ts
@@ -20,6 +20,7 @@ import {hasClosestByAttribute, hasClosestByTag, hasTopClosestByTag} from "../../
import {isTouchDevice} from "../../util/functions";
import {App} from "../../index";
import {refreshFileTree} from "../../dialog/processSystem";
+import {hideTooltip} from "../../dialog/tooltip";
export class Files extends Model {
public element: HTMLElement;
@@ -382,6 +383,7 @@ export class Files extends Model {
return;
}
window.getSelection().removeAllRanges();
+ hideTooltip();
const liElement = hasClosestByTag(event.target, "LI");
if (liElement) {
let selectElements: Element[] = Array.from(this.element.querySelectorAll(".b3-list-item--focus"));
diff --git a/app/src/menus/Menu.ts b/app/src/menus/Menu.ts
index 3e51baa9eaf..4a66455830d 100644
--- a/app/src/menus/Menu.ts
+++ b/app/src/menus/Menu.ts
@@ -256,7 +256,12 @@ export class MenuItem {
const getActionMenu = (element: Element, next: boolean) => {
let actionMenuElement = element;
- while (actionMenuElement && (actionMenuElement.classList.contains("b3-menu__separator") || actionMenuElement.classList.contains("b3-menu__item--readonly"))) {
+ while (actionMenuElement &&
+ (actionMenuElement.classList.contains("b3-menu__separator") ||
+ actionMenuElement.classList.contains("b3-menu__item--readonly") ||
+ // https://github.com/siyuan-note/siyuan/issues/12518
+ actionMenuElement.getBoundingClientRect().height === 0)
+ ) {
if (actionMenuElement.querySelector(".b3-text-field")) {
break;
}
diff --git a/app/src/menus/protyle.ts b/app/src/menus/protyle.ts
index 9bbc223d66b..0830ca69c38 100644
--- a/app/src/menus/protyle.ts
+++ b/app/src/menus/protyle.ts
@@ -25,7 +25,7 @@ import {transaction, updateTransaction} from "../protyle/wysiwyg/transaction";
import {openMenu} from "./commonMenuItem";
import {fetchPost} from "../util/fetch";
import {Constants} from "../constants";
-import {copyPlainText, readText, setStorageVal, writeText} from "../protyle/util/compatibility";
+import {copyPlainText, readText, setStorageVal, updateHotkeyTip, writeText} from "../protyle/util/compatibility";
import {preventScroll} from "../protyle/scroll/preventScroll";
import {onGet} from "../protyle/util/onGet";
import {getAllModels} from "../layout/getAll";
@@ -382,7 +382,7 @@ export const refMenu = (protyle: IProtyle, element: HTMLElement) => {
window.siyuan.menus.menu.append(new MenuItem({
label: window.siyuan.languages.refTab,
icon: "iconEyeoff",
- accelerator: window.siyuan.config.keymap.editor.general.refTab.custom + "/⌘" + window.siyuan.languages.click,
+ accelerator: window.siyuan.config.keymap.editor.general.refTab.custom + "/" + updateHotkeyTip("⌘") + window.siyuan.languages.click,
click() {
checkFold(refBlockId, (zoomIn) => {
openFileById({
@@ -398,7 +398,7 @@ export const refMenu = (protyle: IProtyle, element: HTMLElement) => {
window.siyuan.menus.menu.append(new MenuItem({
label: window.siyuan.languages.insertRight,
icon: "iconLayoutRight",
- accelerator: window.siyuan.config.keymap.editor.general.insertRight.custom + "/⌥" + window.siyuan.languages.click,
+ accelerator: window.siyuan.config.keymap.editor.general.insertRight.custom + "/" + updateHotkeyTip("⌥") + window.siyuan.languages.click,
click() {
checkFold(refBlockId, (zoomIn, action, isRoot) => {
if (!isRoot) {
@@ -417,7 +417,7 @@ export const refMenu = (protyle: IProtyle, element: HTMLElement) => {
window.siyuan.menus.menu.append(new MenuItem({
label: window.siyuan.languages.insertBottom,
icon: "iconLayoutBottom",
- accelerator: window.siyuan.config.keymap.editor.general.insertBottom.custom + (window.siyuan.config.keymap.editor.general.insertBottom.custom ? "/" : "") + "⇧" + window.siyuan.languages.click,
+ accelerator: window.siyuan.config.keymap.editor.general.insertBottom.custom + (window.siyuan.config.keymap.editor.general.insertBottom.custom ? "/" : "") + updateHotkeyTip("⇧") + window.siyuan.languages.click,
click() {
checkFold(refBlockId, (zoomIn, action, isRoot) => {
if (!isRoot) {
@@ -1086,7 +1086,6 @@ export const imgMenu = (protyle: IProtyle, range: Range, assetElement: HTMLEleme
}
}).element);
}
- /// #if !BROWSER
window.siyuan.menus.menu.append(new MenuItem({
label: "OCR",
submenu: [{
@@ -1116,7 +1115,6 @@ export const imgMenu = (protyle: IProtyle, range: Range, assetElement: HTMLEleme
}
}],
}).element);
- /// #endif
window.siyuan.menus.menu.append(new MenuItem({
icon: "iconAlignCenter",
label: window.siyuan.languages.alignCenter,
diff --git a/app/src/protyle/gutter/index.ts b/app/src/protyle/gutter/index.ts
index c3734cd0da4..4ac1f37be01 100644
--- a/app/src/protyle/gutter/index.ts
+++ b/app/src/protyle/gutter/index.ts
@@ -2139,6 +2139,7 @@ export class Gutter {
label: window.siyuan.languages.height,
submenu: styles.concat([{
iconHTML: "",
+ type: "readonly",
label: `
`,
diff --git a/app/src/protyle/header/openTitleMenu.ts b/app/src/protyle/header/openTitleMenu.ts
index 267b283950a..fa47f9ab316 100644
--- a/app/src/protyle/header/openTitleMenu.ts
+++ b/app/src/protyle/header/openTitleMenu.ts
@@ -200,13 +200,13 @@ export const openTitleMenu = (protyle: IProtyle, position: IPosition) => {
/// #if !MOBILE
if (!protyle.model) {
window.siyuan.menus.menu.append(new MenuItem({
- label: window.siyuan.languages.openInNewTab,
- icon: "iconLayoutRight",
+ label: window.siyuan.languages.openBy,
+ icon: "iconOpen",
click() {
openFileById({
app: protyle.app,
id: protyle.block.id,
- action: protyle.block.rootID !== protyle.block.id ? [Constants.CB_GET_ALL] : [Constants.CB_GET_CONTEXT],
+ action: protyle.block.rootID !== protyle.block.id ? [Constants.CB_GET_ALL, Constants.CB_GET_FOCUS] : [Constants.CB_GET_CONTEXT],
});
}
}).element);
diff --git a/app/src/protyle/ui/initUI.ts b/app/src/protyle/ui/initUI.ts
index 572a51bca78..166b0634db9 100644
--- a/app/src/protyle/ui/initUI.ts
+++ b/app/src/protyle/ui/initUI.ts
@@ -8,6 +8,10 @@ import {fetchPost} from "../../util/fetch";
import {lineNumberRender} from "../render/highlightRender";
import {hideMessage, showMessage} from "../../dialog/message";
import {genUUID} from "../../util/genID";
+import {getContenteditableElement, getLastBlock} from "../wysiwyg/getBlock";
+import {genEmptyElement} from "../../block/util";
+import {transaction} from "../wysiwyg/transaction";
+import {focusByRange} from "../util/selection";
export const initUI = (protyle: IProtyle) => {
protyle.contentElement = document.createElement("div");
@@ -91,6 +95,48 @@ export const initUI = (protyle: IProtyle) => {
});
}, Constants.TIMEOUT_LOAD);
}, {passive: false});
+ protyle.contentElement.addEventListener("click", (event: MouseEvent & { target: HTMLElement }) => {
+ // wysiwyg 元素下方点击无效果 https://github.com/siyuan-note/siyuan/issues/12009
+ if (protyle.disabled ||
+ (!event.target.classList.contains("protyle-content") && !event.target.classList.contains("protyle-wysiwyg"))) {
+ return
+ }
+ const lastRect = protyle.wysiwyg.element.lastElementChild.getBoundingClientRect();
+ const range = document.createRange();
+ if (event.y > lastRect.bottom) {
+ const lastEditElement = getContenteditableElement(getLastBlock(protyle.wysiwyg.element.lastElementChild));
+ if (!lastEditElement ||
+ (protyle.wysiwyg.element.lastElementChild.getAttribute("data-type") !== "NodeParagraph" && protyle.wysiwyg.element.getAttribute("data-doc-type") !== "NodeListItem") ||
+ (protyle.wysiwyg.element.lastElementChild.getAttribute("data-type") === "NodeParagraph" && getContenteditableElement(lastEditElement).innerHTML !== "")) {
+ const emptyElement = genEmptyElement(false, false);
+ protyle.wysiwyg.element.insertAdjacentElement("beforeend", emptyElement);
+ transaction(protyle, [{
+ action: "insert",
+ data: emptyElement.outerHTML,
+ id: emptyElement.getAttribute("data-node-id"),
+ previousID: emptyElement.previousElementSibling.getAttribute("data-node-id"),
+ parentID: protyle.block.parentID
+ }], [{
+ action: "delete",
+ id: emptyElement.getAttribute("data-node-id")
+ }]);
+ const emptyEditElement = getContenteditableElement(emptyElement) as HTMLInputElement;
+ range.selectNodeContents(emptyEditElement);
+ range.collapse(true);
+ focusByRange(range);
+ // 需等待 range 更新再次进行渲染
+ if (protyle.options.render.breadcrumb) {
+ setTimeout(() => {
+ protyle.breadcrumb.render(protyle);
+ }, Constants.TIMEOUT_TRANSITION);
+ }
+ } else if (lastEditElement) {
+ range.selectNodeContents(lastEditElement);
+ range.collapse(false);
+ focusByRange(range);
+ }
+ }
+ });
};
export const addLoading = (protyle: IProtyle, msg?: string) => {
diff --git a/app/src/protyle/util/editorCommonEvent.ts b/app/src/protyle/util/editorCommonEvent.ts
index 5c55d0ae168..5da609b8024 100644
--- a/app/src/protyle/util/editorCommonEvent.ts
+++ b/app/src/protyle/util/editorCommonEvent.ts
@@ -797,7 +797,7 @@ export const dropEvent = (protyle: IProtyle, editorElement: HTMLElement) => {
}
const targetElement = editorElement.querySelector(".dragover__left, .dragover__right, .dragover__bottom, .dragover__top");
if (targetElement) {
- targetElement.classList.remove("protyle-wysiwyg--select");
+ targetElement.classList.remove("protyle-wysiwyg--select", "dragover");
targetElement.removeAttribute("select-start");
targetElement.removeAttribute("select-end");
}
@@ -1176,6 +1176,18 @@ export const dropEvent = (protyle: IProtyle, editorElement: HTMLElement) => {
event.preventDefault();
let targetElement = hasClosestByClassName(event.target, "av__row") || hasClosestByClassName(event.target, "av__row--util") || hasClosestBlock(event.target);
const point = {x: event.clientX, y: event.clientY, className: ""};
+
+ // 超级块中有a,b两个段落块,移动到 ab 之间的间隙 targetElement 会变为超级块,需修正为 a
+ if (targetElement && (targetElement.classList.contains("bq") || targetElement.classList.contains("sb") || targetElement.classList.contains("list") || targetElement.classList.contains("li"))) {
+ let prevElement = hasClosestBlock(document.elementFromPoint(point.x, point.y - 6))
+ while (prevElement && targetElement.contains(prevElement)) {
+ if (prevElement.nextElementSibling?.getAttribute("data-node-id")) {
+ targetElement = prevElement;
+ }
+ prevElement = prevElement.parentElement;
+ }
+ }
+
if (!targetElement) {
if (event.clientY > editorElement.lastElementChild.getBoundingClientRect().bottom) {
// 命中底部
@@ -1227,7 +1239,8 @@ export const dropEvent = (protyle: IProtyle, editorElement: HTMLElement) => {
}
} else if (targetElement && gutterType && gutterType.startsWith(`${Constants.SIYUAN_DROP_GUTTER}NodeAttributeViewRowMenu${Constants.ZWSP}`.toLowerCase())) {
// 行只能拖拽当前 av 中
- if (!targetElement.classList.contains("av__row") || !window.siyuan.dragElement.contains(targetElement)) {
+ if ((!targetElement.classList.contains("av__row") && !targetElement.classList.contains("av__row--util")) ||
+ !window.siyuan.dragElement.contains(targetElement)) {
targetElement = false;
}
}
@@ -1247,14 +1260,17 @@ export const dropEvent = (protyle: IProtyle, editorElement: HTMLElement) => {
return;
}
if (point.className) {
- targetElement.classList.add(point.className, "dragover");
+ targetElement.classList.add(point.className);
+ addDragover(targetElement);
return;
}
if (targetElement.getAttribute("data-type") === "NodeListItem" || fileTreeIds.indexOf("-") > -1) {
if (event.clientY > nodeRect.top + nodeRect.height / 2) {
- targetElement.classList.add("dragover__bottom", "dragover");
+ targetElement.classList.add("dragover__bottom");
+ addDragover(targetElement);
} else if (!targetElement.classList.contains("av__row--header")) {
- targetElement.classList.add("dragover__top", "dragover");
+ targetElement.classList.add("dragover__top");
+ addDragover(targetElement);
}
return;
}
@@ -1275,19 +1291,23 @@ export const dropEvent = (protyle: IProtyle, editorElement: HTMLElement) => {
}
if (event.clientX < nodeRect.left + 32 && event.clientX >= nodeRect.left - 1 &&
!targetElement.classList.contains("av__row")) {
- targetElement.classList.add("dragover__left", "dragover");
+ targetElement.classList.add("dragover__left");
+ addDragover(targetElement);
} else if (event.clientX > nodeRect.right - 32 && event.clientX < nodeRect.right &&
!targetElement.classList.contains("av__row")) {
- targetElement.classList.add("dragover__right", "dragover");
+ targetElement.classList.add("dragover__right");
+ addDragover(targetElement);
} else if (targetElement.classList.contains("av__row--header")) {
- targetElement.classList.add("dragover__bottom", "dragover");
+ targetElement.classList.add("dragover__bottom");
} else if (targetElement.classList.contains("av__row--util")) {
- targetElement.previousElementSibling.classList.add("dragover__bottom", "dragover");
+ targetElement.previousElementSibling.classList.add("dragover__bottom");
} else {
if (event.clientY > nodeRect.top + nodeRect.height / 2 && disabledPosition !== "bottom") {
- targetElement.classList.add("dragover__bottom", "dragover");
+ targetElement.classList.add("dragover__bottom");
+ addDragover(targetElement);
} else if (disabledPosition !== "top") {
- targetElement.classList.add("dragover__top", "dragover");
+ targetElement.classList.add("dragover__top");
+ addDragover(targetElement);
}
}
return;
@@ -1362,3 +1382,12 @@ export const dropEvent = (protyle: IProtyle, editorElement: HTMLElement) => {
});
});
};
+
+const addDragover = (element: HTMLElement) => {
+ if (element.classList.contains("sb") ||
+ element.classList.contains("li") ||
+ element.classList.contains("list") ||
+ element.classList.contains("bq")) {
+ element.classList.add("dragover")
+ }
+}
diff --git a/app/src/protyle/wysiwyg/index.ts b/app/src/protyle/wysiwyg/index.ts
index 4de0f96e104..9b213af423a 100644
--- a/app/src/protyle/wysiwyg/index.ts
+++ b/app/src/protyle/wysiwyg/index.ts
@@ -2155,6 +2155,7 @@ export class WYSIWYG {
action,
zoomIn
});
+ window.dispatchEvent(new KeyboardEvent("keydown", {key: "Escape"}));
} else if (event.altKey) {
openFileById({
app: protyle.app,
@@ -2226,8 +2227,11 @@ export class WYSIWYG {
return;
}
- // 如果aLink 为空时,当 data-type="a inline-math" 可继续后续操作
- if (aElement && range.toString() === "" && aLink) {
+ if (aElement &&
+ // https://github.com/siyuan-note/siyuan/issues/11980
+ (event.shiftKey || range.toString() === "") &&
+ // 如果aLink 为空时,当 data-type="a inline-math" 可继续后续操作
+ aLink) {
event.stopPropagation();
event.preventDefault();
openLink(protyle, aLink, event, ctrlIsPressed);
@@ -2515,44 +2519,6 @@ export class WYSIWYG {
return;
}
- // 点击空白
- if (event.target.contains(this.element) && this.element.lastElementChild && !protyle.disabled) {
- const lastRect = this.element.lastElementChild.getBoundingClientRect();
- if (event.y > lastRect.bottom) {
- const lastEditElement = getContenteditableElement(getLastBlock(this.element.lastElementChild));
- if (!lastEditElement ||
- (this.element.lastElementChild.getAttribute("data-type") !== "NodeParagraph" && protyle.wysiwyg.element.getAttribute("data-doc-type") !== "NodeListItem") ||
- (this.element.lastElementChild.getAttribute("data-type") === "NodeParagraph" && getContenteditableElement(lastEditElement).innerHTML !== "")) {
- const emptyElement = genEmptyElement(false, false);
- this.element.insertAdjacentElement("beforeend", emptyElement);
- transaction(protyle, [{
- action: "insert",
- data: emptyElement.outerHTML,
- id: emptyElement.getAttribute("data-node-id"),
- previousID: emptyElement.previousElementSibling.getAttribute("data-node-id"),
- parentID: protyle.block.parentID
- }], [{
- action: "delete",
- id: emptyElement.getAttribute("data-node-id")
- }]);
- const emptyEditElement = getContenteditableElement(emptyElement) as HTMLInputElement;
- range.selectNodeContents(emptyEditElement);
- range.collapse(true);
- focusByRange(range);
- // 需等待 range 更新再次进行渲染
- if (protyle.options.render.breadcrumb) {
- setTimeout(() => {
- protyle.breadcrumb.render(protyle);
- }, Constants.TIMEOUT_TRANSITION);
- }
- } else if (lastEditElement) {
- range.selectNodeContents(lastEditElement);
- range.collapse(false);
- focusByRange(range);
- }
- }
- }
-
setTimeout(() => {
// 选中后,在选中的文字上点击需等待 range 更新
let newRange = getEditorRange(this.element);
diff --git a/kernel/api/filetree.go b/kernel/api/filetree.go
index 405b7d20d34..d1a9c576277 100644
--- a/kernel/api/filetree.go
+++ b/kernel/api/filetree.go
@@ -673,6 +673,12 @@ func createDocWithMd(c *gin.Context) {
return
}
+ tagsArg := arg["tags"]
+ var tags string
+ if nil != tagsArg {
+ tags = tagsArg.(string)
+ }
+
var parentID string
parentIDArg := arg["parentID"]
if nil != parentIDArg {
@@ -706,7 +712,7 @@ func createDocWithMd(c *gin.Context) {
withMath = withMathArg.(bool)
}
- id, err := model.CreateWithMarkdown(notebook, hPath, markdown, parentID, id, withMath)
+ id, err := model.CreateWithMarkdown(tags, notebook, hPath, markdown, parentID, id, withMath)
if err != nil {
ret.Code = -1
ret.Msg = err.Error()
diff --git a/kernel/model/asset_content.go b/kernel/model/asset_content.go
index 628f72383e4..f41e3ef89d6 100644
--- a/kernel/model/asset_content.go
+++ b/kernel/model/asset_content.go
@@ -880,7 +880,7 @@ func (parser *PdfAssetParser) Parse(absPath string) (ret *AssetParseResult) {
res := <-results
pageText[res.pageNo] = res.text
if nil != res.err {
- logging.LogErrorf("convert [%s] of page %d failed: [%s]", tmp, res.pageNo, err)
+ logging.LogErrorf("convert [%s] of page %d failed: [%s]", tmp, res.pageNo, res.err)
}
}
close(results)
diff --git a/kernel/model/file.go b/kernel/model/file.go
index 5765b5d93f0..320eba633f1 100644
--- a/kernel/model/file.go
+++ b/kernel/model/file.go
@@ -1159,7 +1159,7 @@ func CreateDocByMd(boxID, p, title, md string, sorts []string) (tree *parse.Tree
return
}
-func CreateWithMarkdown(boxID, hPath, md, parentID, id string, withMath bool) (retID string, err error) {
+func CreateWithMarkdown(tags, boxID, hPath, md, parentID, id string, withMath bool) (retID string, err error) {
createDocLock.Lock()
defer createDocLock.Unlock()
@@ -1177,6 +1177,19 @@ func CreateWithMarkdown(boxID, hPath, md, parentID, id string, withMath bool) (r
luteEngine.SetHTMLTag2TextMark(true)
dom := luteEngine.Md2BlockDOM(md, false)
retID, err = createDocsByHPath(box.ID, hPath, dom, parentID, id)
+
+ nameValues := map[string]string{}
+ tags = strings.TrimSpace(tags)
+ tags = strings.ReplaceAll(tags, ",", ",")
+ tagArray := strings.Split(tags, ",")
+ var tmp []string
+ for _, tag := range tagArray {
+ tmp = append(tmp, strings.TrimSpace(tag))
+ }
+ tags = strings.Join(tmp, ",")
+ nameValues["tags"] = tags
+ SetBlockAttrs(retID, nameValues)
+
WaitForWritingFiles()
return
}
diff --git a/kernel/model/search.go b/kernel/model/search.go
index a7f7a0f0350..7a0949ae1e4 100644
--- a/kernel/model/search.go
+++ b/kernel/model/search.go
@@ -824,8 +824,30 @@ func replaceNodeTextMarkTextContent(n *ast.Node, method int, keyword string, rep
// Supports replacing text elements with other elements https://github.com/siyuan-note/siyuan/issues/11058
func replaceTextNode(text *ast.Node, method int, keyword string, replacement string, r *regexp.Regexp, luteEngine *lute.Lute) bool {
if 0 == method {
- if bytes.Contains(text.Tokens, []byte(keyword)) {
- newContent := bytes.ReplaceAll(text.Tokens, []byte(keyword), []byte(replacement))
+ newContent := text.Tokens
+ if Conf.Search.CaseSensitive {
+ if bytes.Contains(text.Tokens, []byte(keyword)) {
+ newContent = bytes.ReplaceAll(text.Tokens, []byte(keyword), []byte(replacement))
+ }
+ } else {
+ // 当搜索结果中的文本元素包含大小写混合时替换失败
+ // Replace fails when search results contain mixed case in text elements https://github.com/siyuan-note/siyuan/issues/9171
+ keywords := strings.Split(keyword, " ")
+ // keyword 可能是 "foo Foo" 使用空格分隔的大小写命中情况,这里统一转换小写后去重
+ if 1 < len(keywords) {
+ var lowerKeywords []string
+ for _, k := range keywords {
+ lowerKeywords = append(lowerKeywords, strings.ToLower(k))
+ }
+ lowerKeywords = gulu.Str.RemoveDuplicatedElem(lowerKeywords)
+ keyword = strings.Join(lowerKeywords, " ")
+ }
+
+ if bytes.Contains(bytes.ToLower(text.Tokens), []byte(keyword)) {
+ newContent = replaceCaseInsensitive(text.Tokens, []byte(keyword), []byte(replacement))
+ }
+ }
+ if !bytes.Equal(newContent, text.Tokens) {
tree := parse.Inline("", newContent, luteEngine.ParseOptions)
if nil == tree.Root.FirstChild {
return false
@@ -1818,3 +1840,8 @@ func filterQueryInvisibleChars(query string) string {
query = strings.ReplaceAll(query, "_@full_width_space@_", " ")
return query
}
+
+func replaceCaseInsensitive(input, old, new []byte) []byte {
+ re := regexp.MustCompile("(?i)" + regexp.QuoteMeta(string(old)))
+ return []byte(re.ReplaceAllString(string(input), string(new)))
+}
diff --git a/kernel/model/template.go b/kernel/model/template.go
index 2015711d83d..be8a6a1191d 100644
--- a/kernel/model/template.go
+++ b/kernel/model/template.go
@@ -42,7 +42,7 @@ import (
func RenderGoTemplate(templateContent string) (ret string, err error) {
tmpl := template.New("")
- tplFuncMap := util.BuiltInTemplateFuncs()
+ tplFuncMap := treenode.BuiltInTemplateFuncs()
sql.SQLTemplateFuncs(&tplFuncMap)
tmpl = tmpl.Funcs(tplFuncMap)
tpl, err := tmpl.Parse(templateContent)
@@ -224,7 +224,7 @@ func RenderTemplate(p, id string, preview bool) (tree *parse.Tree, dom string, e
}
goTpl := template.New("").Delims(".action{", "}")
- tplFuncMap := util.BuiltInTemplateFuncs()
+ tplFuncMap := treenode.BuiltInTemplateFuncs()
sql.SQLTemplateFuncs(&tplFuncMap)
goTpl = goTpl.Funcs(tplFuncMap)
tpl, err := goTpl.Funcs(tplFuncMap).Parse(gulu.Str.FromBytes(md))
diff --git a/kernel/sql/av.go b/kernel/sql/av.go
index fa185f0fdd9..4c9ace3ff59 100644
--- a/kernel/sql/av.go
+++ b/kernel/sql/av.go
@@ -404,7 +404,7 @@ func RenderTemplateCol(ial map[string]string, rowValues []*av.KeyValues, tplCont
}
goTpl := template.New("").Delims(".action{", "}")
- tplFuncMap := util.BuiltInTemplateFuncs()
+ tplFuncMap := treenode.BuiltInTemplateFuncs()
SQLTemplateFuncs(&tplFuncMap)
goTpl = goTpl.Funcs(tplFuncMap)
tpl, err := goTpl.Parse(tplContent)
diff --git a/kernel/util/template.go b/kernel/treenode/template.go
similarity index 84%
rename from kernel/util/template.go
rename to kernel/treenode/template.go
index 2f133364b33..84561fc6106 100644
--- a/kernel/util/template.go
+++ b/kernel/treenode/template.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
-package util
+package treenode
import (
"math"
@@ -25,21 +25,23 @@ import (
"github.com/Masterminds/sprig/v3"
"github.com/araddon/dateparse"
"github.com/siyuan-note/logging"
+ "github.com/siyuan-note/siyuan/kernel/util"
"github.com/spf13/cast"
)
func BuiltInTemplateFuncs() (ret template.FuncMap) {
ret = sprig.TxtFuncMap()
- ret["Weekday"] = Weekday
- ret["WeekdayCN"] = WeekdayCN
- ret["WeekdayCN2"] = WeekdayCN2
- ret["ISOWeek"] = ISOWeek
+ ret["Weekday"] = util.Weekday
+ ret["WeekdayCN"] = util.WeekdayCN
+ ret["WeekdayCN2"] = util.WeekdayCN2
+ ret["ISOWeek"] = util.ISOWeek
ret["pow"] = pow
ret["powf"] = powf
ret["log"] = log
ret["logf"] = logf
ret["parseTime"] = parseTime
ret["FormatFloat"] = FormatFloat
+ ret["getHPathByID"] = getHPathByID
return
}
@@ -63,3 +65,12 @@ func parseTime(dateStr string) time.Time {
func FormatFloat(format string, n float64) string {
return humanize.FormatFloat(format, n)
}
+
+func getHPathByID(id string) (ret string) {
+ bt := GetBlockTree(id)
+ if nil == bt {
+ return
+ }
+ ret = bt.HPath
+ return
+}