Merge branch 'dev' into dev-tooltip

This commit is contained in:
Jeffrey Chen 2024-12-20 19:51:16 +08:00 committed by GitHub
commit f4f2918706
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
30 changed files with 480 additions and 309 deletions

View file

@ -862,7 +862,7 @@ app.whenReady().then(() => {
}
break;
case "setTrafficLightPosition":
if (!currentWindow) {
if (!currentWindow || !currentWindow.setWindowButtonPosition) {
return;
}
if (new URL(currentWindow.getURL()).pathname === "/stage/build/app/window.html") {

View file

@ -85,7 +85,7 @@
transition: var(--b3-transition), opacity .3s cubic-bezier(0, 0, .2, 1) 0ms;
line-height: 14px;
&:hover:not([disabled]):not(.ft__primary),
&:hover:not([disabled]):not(.ft__primary):not(.block__icon--warning),
&--active {
color: var(--b3-theme-on-background);
background-color: var(--b3-list-icon-hover);
@ -100,6 +100,11 @@
}
}
&--warning:hover {
color: var(--b3-theme-error);
background-color: var(--b3-list-icon-hover);
}
svg {
height: 14px;
width: 14px;

View file

@ -409,14 +409,14 @@
transition: var(--b3-transition);
}
&:hover,
&:hover:not(.dock__item--activefocus),
&--active {
background-color: var(--b3-theme-background-light);
}
&--activefocus {
color: var(--b3-theme-on-primary) !important;
background-color: var(--b3-theme-primary) !important;
color: var(--b3-theme-on-primary);
background-color: var(--b3-theme-primary);
}
}
}

View file

@ -1,5 +1,6 @@
@font-face {
font-family: 'JetBrainsMono-Regular';
/* webpackIgnore: true */
src: url(../../../appearance/fonts/JetBrainsMono-2.304/JetBrainsMono-Regular.woff2) format('woff2');
}

View file

@ -0,0 +1,9 @@
export const img3115 = (imgElement: HTMLElement) => {
// 移除 3.1.15 以前 .img width 样式
if (imgElement.style.minWidth) {
// 居中需要 minWidth 样式,不能移除 style 属性
imgElement.style.width = "";
} else {
imgElement.removeAttribute("style");
}
}

View file

@ -1366,14 +1366,14 @@ export const windowKeyDown = (app: App, event: KeyboardEvent) => {
return;
}
// https://github.com/siyuan-note/siyuan/issues/8913#issuecomment-1679720605
const confirmElement = document.querySelector("#confirmDialogConfirmBtn");
if (confirmElement) {
const confirmDialogElement = document.querySelector('.b3-dialog--open[data-key="dialog-confirm"]');
if (confirmDialogElement) {
if (event.key === "Enter") {
confirmElement.dispatchEvent(new CustomEvent("click"));
confirmDialogElement.dispatchEvent(new CustomEvent("click", {detail: event.key}));
event.preventDefault();
return;
} else if (event.key === "Escape") {
confirmElement.previousElementSibling.previousElementSibling.dispatchEvent(new CustomEvent("click"));
confirmDialogElement.dispatchEvent(new CustomEvent("click", {detail: event.key}));
event.preventDefault();
return;
}

View file

@ -16,23 +16,31 @@ export const confirmDialog = (title: string, text: string,
<div class="ft__breakword">${text}</div>
</div>
<div class="b3-dialog__action">
<button class="b3-button b3-button--cancel">${window.siyuan.languages.cancel}</button><div class="fn__space"></div>
<button class="b3-button b3-button--cancel" id="cancelDialogConfirmBtn">${window.siyuan.languages.cancel}</button><div class="fn__space"></div>
<button class="b3-button ${isDelete ? "b3-button--remove" : "b3-button--text"}" id="confirmDialogConfirmBtn">${window.siyuan.languages[isDelete ? "delete" : "confirm"]}</button>
</div>`,
width: isMobile() ? "92vw" : "520px",
});
const btnsElement = dialog.element.querySelectorAll(".b3-button");
btnsElement[0].addEventListener("click", () => {
if (cancel) {
cancel(dialog);
dialog.element.addEventListener("click", (event) => {
let target = event.target as HTMLElement;
const isDispatch = typeof event.detail === "string";
while (target && !target.isSameNode(dialog.element) || isDispatch) {
if (target.id === "cancelDialogConfirmBtn" || (isDispatch && event.detail=== "Escape")) {
if (cancel) {
cancel(dialog);
}
dialog.destroy();
break;
} else if (target.id === "confirmDialogConfirmBtn" || (isDispatch && event.detail=== "Enter")) {
if (confirm) {
confirm(dialog);
}
dialog.destroy();
break;
}
target = target.parentElement;
}
dialog.destroy();
});
btnsElement[1].addEventListener("click", () => {
if (confirm) {
confirm(dialog);
}
dialog.destroy();
});
dialog.element.setAttribute("data-key", Constants.DIALOG_CONFIRM);
};

View file

@ -45,6 +45,7 @@ const renderDoc = (element: HTMLElement, currentPage: number, id: string) => {
pageNumElement.setAttribute("data-totalpage", response.data.pageCount.toString());
pageNumElement.textContent = currentPage.toString();
const pageInfoElement = nextElement.nextElementSibling.nextElementSibling;
pageInfoElement.classList.remove("fn__none");
pageInfoElement.textContent = window.siyuan.languages.pageCountAndHistoryCount.replace("${x}", response.data.pageCount).replace("${y}", response.data.totalCount);
if (response.data.histories.length === 0) {
listElement.innerHTML = `<li class="b3-list--empty">${window.siyuan.languages.emptyContent}</li>`;
@ -76,7 +77,7 @@ export const openDocHistory = (options: {
<button class="b3-button b3-button--text ft__selectnone" data-type="jumpRepoPage" disabled>1</button>
<span data-type="docnext" class="block__icon block__icon--show b3-tooltips b3-tooltips__e" disabled="disabled" aria-label="${window.siyuan.languages.nextLabel}"><svg><use xlink:href="#iconRight"></use></svg></span>
<span class="fn__space"></span>
<span class="ft__on-surface fn__flex-shrink ft__selectnone">${window.siyuan.languages.pageCountAndHistoryCount}</span>
<span class="ft__on-surface fn__flex-shrink ft__selectnone fn__none">${window.siyuan.languages.pageCountAndHistoryCount}</span>
<span class="fn__space"></span>
<div class="fn__flex-1"></div>
<select data-type="opselect" class="b3-select">

View file

@ -59,6 +59,7 @@ import {Menu} from "../plugin/Menu";
import {getFirstBlock} from "../protyle/wysiwyg/getBlock";
import {popSearch} from "../mobile/menu/search";
import {showMessage} from "../dialog/message";
import {img3115} from "../boot/compatibleVersion";
const renderAssetList = (element: Element, k: string, position: IPosition, exts: string[] = []) => {
fetchPost("/api/search/searchAsset", {
@ -1188,8 +1189,7 @@ export const imgMenu = (protyle: IProtyle, range: Range, assetElement: HTMLEleme
rangeElement.value = "0";
rangeElement.parentElement.setAttribute("aria-label", inputElement.value ? (inputElement.value + "px") : window.siyuan.languages.default);
// 历史兼容
assetElement.removeAttribute("style");
img3115(assetElement)
imgElement.parentElement.style.width = inputElement.value ? (inputElement.value + "px") : "";
imgElement.style.height = "";
});
@ -1220,8 +1220,7 @@ export const imgMenu = (protyle: IProtyle, range: Range, assetElement: HTMLEleme
bind(element) {
rangeElement = element.querySelector("input");
rangeElement.addEventListener("input", () => {
// 历史兼容
assetElement.removeAttribute("style");
img3115(assetElement)
imgElement.parentElement.style.width = rangeElement.value + "%";
imgElement.style.height = "";
rangeElement.parentElement.setAttribute("aria-label", `${rangeElement.value}%`);
@ -1255,8 +1254,7 @@ export const imgMenu = (protyle: IProtyle, range: Range, assetElement: HTMLEleme
rangeHeightElement.parentElement.setAttribute("aria-label", inputElement.value ? (inputElement.value + "px") : window.siyuan.languages.default);
imgElement.style.height = inputElement.value ? (inputElement.value + "px") : "";
// 历史兼容
assetElement.removeAttribute("style");
img3115(assetElement)
imgElement.parentElement.style.width = "";
});
inputElement.addEventListener("blur", () => {
@ -1286,8 +1284,7 @@ export const imgMenu = (protyle: IProtyle, range: Range, assetElement: HTMLEleme
bind(element) {
rangeHeightElement = element.querySelector("input");
rangeHeightElement.addEventListener("input", () => {
// 历史兼容
assetElement.removeAttribute("style");
img3115(assetElement)
imgElement.parentElement.style.width = "";
imgElement.style.height = rangeHeightElement.value + "vh";
rangeHeightElement.parentElement.setAttribute("aria-label", `${rangeHeightElement.value}%`);
@ -1828,8 +1825,7 @@ const genImageWidthMenu = (label: string, imgElement: HTMLElement, protyle: IPro
label,
click() {
nodeElement.setAttribute("updated", dayjs().format("YYYYMMDDHHmmss"));
// 历史兼容
imgElement.parentElement.parentElement.removeAttribute("style");
img3115(imgElement.parentElement.parentElement)
imgElement.parentElement.style.width = label === window.siyuan.languages.default ? "" : label;
imgElement.style.height = "";
updateTransaction(protyle, id, nodeElement.outerHTML, html);
@ -1845,8 +1841,7 @@ const genImageHeightMenu = (label: string, imgElement: HTMLElement, protyle: IPr
click() {
nodeElement.setAttribute("updated", dayjs().format("YYYYMMDDHHmmss"));
imgElement.style.height = label === window.siyuan.languages.default ? "" : parseInt(label) + "vh";
// 历史兼容
imgElement.parentElement.parentElement.removeAttribute("style");
img3115(imgElement.parentElement.parentElement)
imgElement.parentElement.style.width = "";
updateTransaction(protyle, id, nodeElement.outerHTML, html);
focusBlock(nodeElement);

View file

@ -168,7 +168,7 @@ const renderPDF = async (id: string) => {
.b3-label:last-child {
border-bottom: none;
}
${setInlineStyle(false)}
${await setInlineStyle(false)}
${document.getElementById("pluginsStyle").innerHTML}
${getSnippetCSS()}
</style>

View file

@ -43,6 +43,7 @@ import {setStorageVal} from "./util/compatibility";
import {merge} from "./util/merge";
import {getAllModels} from "../layout/getAll";
import {isSupportCSSHL} from "./render/searchMarkRender";
import {renderAVAttribute} from "./render/av/blockAttr";
export class Protyle {
@ -463,4 +464,8 @@ export class Protyle {
public enable() {
enableProtyle(this.protyle);
}
public renderAVAttribute(element: HTMLElement, id: string, cb?: (element: HTMLElement) => void) {
renderAVAttribute(element, id, this.protyle, cb);
}
}

View file

@ -186,7 +186,7 @@ export const renderAVAttribute = (element: HTMLElement, id: string, protyle: IPr
</div>
<div class="fn__flex-1"></div>
<span class="fn__space"></span>
<span data-type="remove" class="block__icon block__icon--show ariaLabel ariaLabel--warning" data-position="west" aria-label="${window.siyuan.languages.removeAV}"><svg><use xlink:href="#iconTrashcan"></use></svg></span>
<span data-type="remove" class="block__icon block__icon--warning block__icon--show ariaLabel" data-position="west" aria-label="${window.siyuan.languages.removeAV}"><svg><use xlink:href="#iconTrashcan"></use></svg></span>
</div>`;
table.keyValues?.forEach(item => {
innerHTML += `<div class="block__icons av__row" data-id="${id}" data-col-id="${item.key.id}">
@ -358,6 +358,10 @@ class="fn__flex-1 fn__flex${["url", "text", "number", "email", "phone"].includes
action: "removeAttrViewBlock",
srcIDs: [id],
avID: blockElement.dataset.avId,
}, {
action: "doUpdateUpdated",
id,
data: dayjs().format("YYYYMMDDHHmmss"),
}]);
blockElement.remove();
if (!element.innerHTML) {

View file

@ -968,9 +968,10 @@ export const showColMenu = (protyle: IProtyle, blockElement: Element, cellElemen
});
dialog.element.addEventListener("click", (event) => {
let target = event.target as HTMLElement;
while (target && !target.isSameNode(dialog.element)) {
const isDispatch = typeof event.detail === "string";
while (target && !target.isSameNode(dialog.element) || isDispatch) {
const action = target.getAttribute("data-action");
if (action === "delete") {
if (action === "delete" || (isDispatch && event.detail === "Enter")) {
removeColByMenu({
protyle,
colId,
@ -998,13 +999,15 @@ export const showColMenu = (protyle: IProtyle, blockElement: Element, cellElemen
});
dialog.destroy();
break;
} else if (target.classList.contains("b3-button--cancel")) {
} else if (target.classList.contains("b3-button--cancel") || (isDispatch && event.detail === "Escape")) {
dialog.destroy();
break;
}
target = target.parentElement;
}
});
dialog.element.querySelector("button").focus()
dialog.element.setAttribute("data-key", Constants.DIALOG_CONFIRM);
return;
}
}

View file

@ -1106,11 +1106,12 @@ export const openMenuPanel = (options: {
<button class="fn__block b3-button b3-button--cancel">${window.siyuan.languages.cancel}</button>
</div>`,
});
dialog.element.addEventListener("click", (event) => {
let target = event.target as HTMLElement;
while (target && !target.isSameNode(dialog.element)) {
dialog.element.addEventListener("click", (dialogEvent) => {
let target = dialogEvent.target as HTMLElement;
const isDispatch = typeof dialogEvent.detail === "string";
while (target && !target.isSameNode(dialog.element) || isDispatch) {
const action = target.getAttribute("data-action");
if (action === "delete") {
if (action === "delete" || (isDispatch && dialogEvent.detail === "Enter")) {
removeCol({
protyle: options.protyle,
data,
@ -1140,13 +1141,14 @@ export const openMenuPanel = (options: {
});
dialog.destroy();
break;
} else if (target.classList.contains("b3-button--cancel")) {
} else if (target.classList.contains("b3-button--cancel") || (isDispatch && dialogEvent.detail === "Escape")) {
dialog.destroy();
break;
}
target = target.parentElement;
}
});
dialog.element.setAttribute("data-key", Constants.DIALOG_CONFIRM);
} else {
removeCol({
protyle: options.protyle,

View file

@ -139,7 +139,7 @@ export const setColOption = (protyle: IProtyle, data: IAV, target: HTMLElement,
if ((name === inputElement.value && desc === descElement.value) || !inputElement.value) {
return;
}
// 不判断重名 https://github.com/siyuan-note/siyuan/issues/11484
// cell 不判断重名 https://github.com/siyuan-note/siyuan/issues/11484
transaction(protyle, [{
action: "updateAttrViewColOption",
id: colId,
@ -163,13 +163,23 @@ export const setColOption = (protyle: IProtyle, data: IAV, target: HTMLElement,
}]);
data.view.columns.find(column => {
if (column.id === colId) {
// 重名不进行更新 https://github.com/siyuan-note/siyuan/issues/13554
let hasName = false;
column.options.find((item) => {
if (item.name === name) {
item.name = inputElement.value;
item.desc = descElement.value;
if (item.name === inputElement.value) {
hasName = true;
return true;
}
});
if (!hasName) {
column.options.find((item) => {
if (item.name === name) {
item.name = inputElement.value;
item.desc = descElement.value;
return true;
}
});
}
return true;
}
});

View file

@ -395,6 +395,10 @@ export const paste = async (protyle: IProtyle, event: (ClipboardEvent | DragEven
const range = getEditorRange(protyle.wysiwyg.element);
if (nodeElement.getAttribute("data-type") === "NodeCodeBlock" ||
protyle.toolbar.getCurrentType(range).includes("code")) {
// https://github.com/siyuan-note/siyuan/issues/13552
textPlain = textPlain.replace(/\u200D```/g, "```");
textPlain = textPlain.replace(/```/g, "\u200D```");
insertHTML(textPlain, protyle);
return;
} else if (siyuanHTML) {
@ -434,6 +438,10 @@ export const paste = async (protyle: IProtyle, event: (ClipboardEvent | DragEven
// 复制 HTML 块粘贴出来的不是 HTML 块 https://github.com/siyuan-note/siyuan/issues/12994
tempInnerHTML = Lute.UnEscapeHTMLStr(tempInnerHTML);
}
// https://github.com/siyuan-note/siyuan/issues/13552
tempInnerHTML = tempInnerHTML.replace(/\u200D```/g, "```");
insertHTML(tempInnerHTML, protyle, isBlock, false, true);
}
filterClipboardHint(protyle, protyle.lute.BlockDOM2StdMd(tempInnerHTML));
@ -531,6 +539,10 @@ export const paste = async (protyle: IProtyle, event: (ClipboardEvent | DragEven
}
}
}
// https://github.com/siyuan-note/siyuan/issues/13552
textPlain = textPlain.replace(/\u200D```/g, "```");
const textPlainDom = protyle.lute.Md2BlockDOM(textPlain);
insertHTML(textPlainDom, protyle, false, false, true);
filterClipboardHint(protyle, textPlain);

View file

@ -95,6 +95,7 @@ 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";
export class WYSIWYG {
public lastHTMLs: { [key: string]: string } = {};
@ -669,14 +670,17 @@ export class WYSIWYG {
const dragElement = target.previousElementSibling as HTMLElement;
const dragWidth = dragElement.clientWidth;
const dragHeight = dragElement.clientHeight;
const imgElement = dragElement.parentElement.parentElement
if (dragElement.tagName === "IMG") {
img3115(imgElement)
}
documentSelf.onmousemove = (moveEvent: MouseEvent) => {
if (dragElement.tagName === "IMG") {
dragElement.style.height = "";
// 历史兼容
dragElement.parentElement.parentElement.removeAttribute("style");
}
if (moveEvent.clientX > x - dragWidth + 8 && moveEvent.clientX < mostRight) {
const multiple = ((dragElement.tagName === "IMG" && !dragElement.parentElement.parentElement.style.minWidth && nodeElement.style.textAlign !== "center") || !isCenter) ? 1 : 2;
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 {
@ -1484,7 +1488,7 @@ export class WYSIWYG {
}
});
tableSelectElement.removeAttribute("style");
if (getSelection().rangeCount>0) {
if (getSelection().rangeCount > 0) {
const range = getSelection().getRangeAt(0);
if (nodeElement.contains(range.startContainer)) {
range.insertNode(document.createElement("wbr"));

View file

@ -14,7 +14,7 @@ import {isPaidUser} from "../util/needSubscribe";
import {showMessage} from "../dialog/message";
import {saveAssetKeyList} from "./toggleHistory";
export const openSearchAsset = (element: Element, isStick: boolean) => {
export const openSearchAsset = (element: HTMLElement, isStick: boolean) => {
/// #if !MOBILE
window.siyuan.menus.menu.remove();
element.previousElementSibling.classList.add("fn__none");
@ -133,7 +133,7 @@ export const openSearchAsset = (element: Element, isStick: boolean) => {
nextElement.classList.remove("fn__flex-1");
nextElement.style[direction === "lr" ? "width" : "height"] = nextSize + "px";
element.style.userSelect = "none";
documentSelf.onmousemove = (moveEvent: MouseEvent) => {
moveEvent.preventDefault();
moveEvent.stopPropagation();
@ -146,6 +146,7 @@ export const openSearchAsset = (element: Element, isStick: boolean) => {
};
documentSelf.onmouseup = () => {
element.style.userSelect = "none";
documentSelf.onmousemove = null;
documentSelf.onmouseup = null;
documentSelf.ondragstart = null;

View file

@ -10,7 +10,7 @@ import {Protyle} from "../protyle";
import {resize} from "../protyle/util/resize";
import {Menu} from "../plugin/Menu";
export const openSearchUnRef = (element: Element, editor: Protyle) => {
export const openSearchUnRef = (element: HTMLElement, editor: Protyle) => {
window.siyuan.menus.menu.remove();
element.previousElementSibling.previousElementSibling.classList.add("fn__none");
element.classList.remove("fn__none");
@ -46,7 +46,7 @@ export const openSearchUnRef = (element: Element, editor: Protyle) => {
nextElement.classList.remove("fn__flex-1");
nextElement.style[direction === "lr" ? "width" : "height"] = nextSize + "px";
element.style.userSelect = "none";
documentSelf.onmousemove = (moveEvent: MouseEvent) => {
moveEvent.preventDefault();
moveEvent.stopPropagation();
@ -59,6 +59,7 @@ export const openSearchUnRef = (element: Element, editor: Protyle) => {
};
documentSelf.onmouseup = () => {
element.style.userSelect = "";
documentSelf.onmousemove = null;
documentSelf.onmouseup = null;
documentSelf.ondragstart = null;

View file

@ -85,7 +85,7 @@ export const openGlobalSearch = (app: App, text: string, replace: boolean, searc
};
// closeCB 不存在为页签搜索
export const genSearch = (app: App, config: Config.IUILayoutTabSearchConfig, element: Element, closeCB?: () => void) => {
export const genSearch = (app: App, config: Config.IUILayoutTabSearchConfig, element: HTMLElement, closeCB?: () => void) => {
let methodText = window.siyuan.languages.keyword;
if (config.method === 1) {
methodText = window.siyuan.languages.querySyntax;
@ -312,7 +312,7 @@ export const genSearch = (app: App, config: Config.IUILayoutTabSearchConfig, ele
nextElement.classList.remove("fn__flex-1");
nextElement.style[direction === "lr" ? "width" : "height"] = nextSize + "px";
element.style.userSelect = "none";
documentSelf.onmousemove = (moveEvent: MouseEvent) => {
moveEvent.preventDefault();
moveEvent.stopPropagation();
@ -325,6 +325,7 @@ export const genSearch = (app: App, config: Config.IUILayoutTabSearchConfig, ele
};
documentSelf.onmouseup = () => {
element.style.userSelect = "";
documentSelf.onmousemove = null;
documentSelf.onmouseup = null;
documentSelf.ondragstart = null;
@ -339,8 +340,8 @@ export const genSearch = (app: App, config: Config.IUILayoutTabSearchConfig, ele
});
const localSearch = window.siyuan.storage[Constants.LOCAL_SEARCHASSET] as ISearchAssetOption;
const assetsElement = element.querySelector("#searchAssets");
const unRefPanelElement = element.querySelector("#searchUnRefPanel");
const assetsElement = element.querySelector("#searchAssets") as HTMLElement;
const unRefPanelElement = element.querySelector("#searchUnRefPanel") as HTMLElement;
element.addEventListener("click", (event: MouseEvent) => {
let target = event.target as HTMLElement;
const searchPathInputElement = element.querySelector("#searchPathInput");

View file

@ -23,7 +23,6 @@ import (
"path/filepath"
"sort"
"strings"
"time"
"github.com/88250/gulu"
"github.com/88250/lute/ast"
@ -366,129 +365,7 @@ func SaveAttributeView(av *AttributeView) (err error) {
}
// 做一些数据兼容和订正处理
now := util.CurrentTimeMillis()
for _, kv := range av.KeyValues {
switch kv.Key.Type {
case KeyTypeBlock:
// 补全 block 的创建时间和更新时间
for _, v := range kv.Values {
if 0 == v.Block.Created {
logging.LogWarnf("block [%s] created time is empty", v.BlockID)
if "" == v.Block.ID {
v.Block.ID = v.BlockID
if "" == v.Block.ID {
v.Block.ID = ast.NewNodeID()
v.BlockID = v.Block.ID
}
}
createdStr := v.Block.ID[:len("20060102150405")]
created, parseErr := time.ParseInLocation("20060102150405", createdStr, time.Local)
if nil == parseErr {
v.Block.Created = created.UnixMilli()
} else {
v.Block.Created = now
}
}
if 0 == v.Block.Updated {
logging.LogWarnf("block [%s] updated time is empty", v.BlockID)
v.Block.Updated = v.Block.Created
}
}
case KeyTypeNumber:
for _, v := range kv.Values {
if nil != v.Number && 0 != v.Number.Content && !v.Number.IsNotEmpty {
v.Number.IsNotEmpty = true
}
}
}
for _, v := range kv.Values {
if "" == kv.Key.ID {
kv.Key.ID = ast.NewNodeID()
for _, val := range kv.Values {
val.KeyID = kv.Key.ID
}
if "" == v.KeyID {
logging.LogWarnf("value [%s] key id is empty", v.ID)
v.KeyID = kv.Key.ID
}
// 校验日期 IsNotEmpty
if KeyTypeDate == kv.Key.Type {
if nil != v.Date && 0 != v.Date.Content && !v.Date.IsNotEmpty {
v.Date.IsNotEmpty = true
}
}
// 校验数字 IsNotEmpty
if KeyTypeNumber == kv.Key.Type {
if nil != v.Number && 0 != v.Number.Content && !v.Number.IsNotEmpty {
v.Number.IsNotEmpty = true
}
}
// 清空关联实际值
if KeyTypeRelation == kv.Key.Type {
v.Relation.Contents = nil
}
// 清空汇总实际值
if KeyTypeRollup == kv.Key.Type {
v.Rollup.Contents = nil
}
for _, view := range av.Views {
switch view.LayoutType {
case LayoutTypeTable:
for _, column := range view.Table.Columns {
if "" == column.ID {
column.ID = kv.Key.ID
break
}
}
}
}
}
// 补全值的创建时间和更新时间
if "" == v.ID {
logging.LogWarnf("value id is empty")
v.ID = ast.NewNodeID()
}
if 0 == v.CreatedAt {
logging.LogWarnf("value [%s] created time is empty", v.ID)
createdStr := v.ID[:len("20060102150405")]
created, parseErr := time.ParseInLocation("20060102150405", createdStr, time.Local)
if nil == parseErr {
v.CreatedAt = created.UnixMilli()
} else {
v.CreatedAt = now
}
}
if 0 == v.UpdatedAt {
logging.LogWarnf("value [%s] updated time is empty", v.ID)
v.UpdatedAt = v.CreatedAt
}
}
}
// 补全过滤器 Value
for _, view := range av.Views {
if nil != view.Table {
for _, f := range view.Table.Filters {
if nil != f.Value {
continue
}
if k, _ := av.GetKey(f.Column); nil != k {
f.Value = &Value{Type: k.Type}
}
}
}
}
UpgradeSpec(av)
// 值去重
blockValues := av.GetBlockKeyValues()

160
kernel/av/av_fix.go Normal file
View file

@ -0,0 +1,160 @@
// SiYuan - Refactor your thinking
// Copyright (c) 2020-present, b3log.org
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package av
import (
"github.com/88250/lute/ast"
"github.com/siyuan-note/logging"
"github.com/siyuan-note/siyuan/kernel/util"
"time"
)
func UpgradeSpec(av *AttributeView) {
upgradeSpec1(av)
}
func upgradeSpec1(av *AttributeView) {
if 1 <= av.Spec {
return
}
now := util.CurrentTimeMillis()
for _, kv := range av.KeyValues {
switch kv.Key.Type {
case KeyTypeBlock:
// 补全 block 的创建时间和更新时间
for _, v := range kv.Values {
if 0 == v.Block.Created {
logging.LogWarnf("block [%s] created time is empty", v.BlockID)
if "" == v.Block.ID {
v.Block.ID = v.BlockID
if "" == v.Block.ID {
v.Block.ID = ast.NewNodeID()
v.BlockID = v.Block.ID
}
}
createdStr := v.Block.ID[:len("20060102150405")]
created, parseErr := time.ParseInLocation("20060102150405", createdStr, time.Local)
if nil == parseErr {
v.Block.Created = created.UnixMilli()
} else {
v.Block.Created = now
}
}
if 0 == v.Block.Updated {
logging.LogWarnf("block [%s] updated time is empty", v.BlockID)
v.Block.Updated = v.Block.Created
}
}
case KeyTypeNumber:
for _, v := range kv.Values {
if nil != v.Number && 0 != v.Number.Content && !v.Number.IsNotEmpty {
v.Number.IsNotEmpty = true
}
}
}
for _, v := range kv.Values {
if "" == kv.Key.ID {
kv.Key.ID = ast.NewNodeID()
for _, val := range kv.Values {
val.KeyID = kv.Key.ID
}
if "" == v.KeyID {
logging.LogWarnf("value [%s] key id is empty", v.ID)
v.KeyID = kv.Key.ID
}
// 校验日期 IsNotEmpty
if KeyTypeDate == kv.Key.Type {
if nil != v.Date && 0 != v.Date.Content && !v.Date.IsNotEmpty {
v.Date.IsNotEmpty = true
}
}
// 校验数字 IsNotEmpty
if KeyTypeNumber == kv.Key.Type {
if nil != v.Number && 0 != v.Number.Content && !v.Number.IsNotEmpty {
v.Number.IsNotEmpty = true
}
}
// 清空关联实际值
if KeyTypeRelation == kv.Key.Type {
v.Relation.Contents = nil
}
// 清空汇总实际值
if KeyTypeRollup == kv.Key.Type {
v.Rollup.Contents = nil
}
for _, view := range av.Views {
switch view.LayoutType {
case LayoutTypeTable:
for _, column := range view.Table.Columns {
if "" == column.ID {
column.ID = kv.Key.ID
break
}
}
}
}
}
// 补全值的创建时间和更新时间
if "" == v.ID {
logging.LogWarnf("value id is empty")
v.ID = ast.NewNodeID()
}
if 0 == v.CreatedAt {
logging.LogWarnf("value [%s] created time is empty", v.ID)
createdStr := v.ID[:len("20060102150405")]
created, parseErr := time.ParseInLocation("20060102150405", createdStr, time.Local)
if nil == parseErr {
v.CreatedAt = created.UnixMilli()
} else {
v.CreatedAt = now
}
}
if 0 == v.UpdatedAt {
logging.LogWarnf("value [%s] updated time is empty", v.ID)
v.UpdatedAt = v.CreatedAt
}
}
}
// 补全过滤器 Value
for _, view := range av.Views {
if nil != view.Table {
for _, f := range view.Table.Filters {
if nil != f.Value {
continue
}
if k, _ := av.GetKey(f.Column); nil != k {
f.Value = &Value{Type: k.Type}
}
}
}
}
av.Spec = 1
}

View file

@ -56,11 +56,11 @@ require (
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06
github.com/sashabaranov/go-openai v1.29.1
github.com/shirou/gopsutil/v3 v3.24.5
github.com/siyuan-note/dejavu v0.0.0-20241218090338-be682e478d2e
github.com/siyuan-note/dejavu v0.0.0-20241220041421-b6410d912871
github.com/siyuan-note/encryption v0.0.0-20231219001248-1e028a4d13b4
github.com/siyuan-note/eventbus v0.0.0-20240627125516-396fdb0f0f97
github.com/siyuan-note/filelock v0.0.0-20241212013445-c66518cdacfa
github.com/siyuan-note/httpclient v0.0.0-20241218090012-d3bf82b2e5be
github.com/siyuan-note/httpclient v0.0.0-20241220030420-75c76bea8a71
github.com/siyuan-note/logging v0.0.0-20241218085028-6514639a9742
github.com/siyuan-note/riff v0.0.0-20241217095231-017015753318
github.com/spf13/cast v1.7.0
@ -73,7 +73,7 @@ require (
golang.org/x/image v0.21.0
golang.org/x/mobile v0.0.0-20240520174638-fa72addaaa1b
golang.org/x/mod v0.22.0
golang.org/x/net v0.32.0
golang.org/x/net v0.33.0
golang.org/x/text v0.21.0
golang.org/x/time v0.6.0
gopkg.in/yaml.v3 v3.0.1
@ -90,7 +90,24 @@ require (
github.com/andybalholm/brotli v1.1.1 // indirect
github.com/andybalholm/cascadia v1.3.2 // indirect
github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef // indirect
github.com/aws/aws-sdk-go v1.55.5 // indirect
github.com/aws/aws-sdk-go-v2 v1.32.7 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 // indirect
github.com/aws/aws-sdk-go-v2/config v1.28.7 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.48 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.26 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.7 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7 // indirect
github.com/aws/aws-sdk-go-v2/service/s3 v1.71.1 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 // indirect
github.com/aws/smithy-go v1.22.1 // indirect
github.com/bytedance/sonic v1.12.4 // indirect
github.com/bytedance/sonic/loader v0.2.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
@ -121,7 +138,6 @@ require (
github.com/hhrutter/tiff v1.0.1 // indirect
github.com/huandu/xstrings v1.5.0 // indirect
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/jolestar/go-commons-pool/v2 v2.1.2 // indirect
github.com/juju/errors v1.0.0 // indirect
github.com/klauspost/compress v1.17.11 // indirect
@ -137,7 +153,7 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/onsi/ginkgo/v2 v2.22.0 // indirect
github.com/onsi/ginkgo/v2 v2.22.1 // indirect
github.com/otiai10/gosseract/v2 v2.4.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/pkg/errors v0.9.1 // indirect
@ -172,7 +188,7 @@ require (
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/tools v0.28.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
google.golang.org/protobuf v1.35.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)

View file

@ -51,8 +51,42 @@ github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhP
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw=
github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef h1:2JGTg6JapxP9/R33ZaagQtAM4EkkSYnIAlOG5EI8gkM=
github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef/go.mod h1:JS7hed4L1fj0hXcyEejnW57/7LCetXggd+vwrRnYeII=
github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU=
github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/aws/aws-sdk-go-v2 v1.32.7 h1:ky5o35oENWi0JYWUZkB7WYvVPP+bcRF5/Iq7JWSb5Rw=
github.com/aws/aws-sdk-go-v2 v1.32.7/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 h1:lL7IfaFzngfx0ZwUGOZdsFFnQ5uLvR0hWqqhyE7Q9M8=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7/go.mod h1:QraP0UcVlQJsmHfioCrveWOC1nbiWUl3ej08h4mXWoc=
github.com/aws/aws-sdk-go-v2/config v1.28.7 h1:GduUnoTXlhkgnxTD93g1nv4tVPILbdNQOzav+Wpg7AE=
github.com/aws/aws-sdk-go-v2/config v1.28.7/go.mod h1:vZGX6GVkIE8uECSUHB6MWAUsd4ZcG2Yq/dMa4refR3M=
github.com/aws/aws-sdk-go-v2/credentials v1.17.48 h1:IYdLD1qTJ0zanRavulofmqut4afs45mOWEI+MzZtTfQ=
github.com/aws/aws-sdk-go-v2/credentials v1.17.48/go.mod h1:tOscxHN3CGmuX9idQ3+qbkzrjVIx32lqDSU1/0d/qXs=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22 h1:kqOrpojG71DxJm/KDPO+Z/y1phm1JlC8/iT+5XRmAn8=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22/go.mod h1:NtSFajXVVL8TA2QNngagVZmUtXciyrHOt7xgz4faS/M=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26 h1:I/5wmGMffY4happ8NOCuIUEWGUvvFp5NSeQcXl9RHcI=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26/go.mod h1:FR8f4turZtNy6baO0KJ5FJUmXH/cSkI9fOngs0yl6mA=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26 h1:zXFLuEuMMUOvEARXFUVJdfqZ4bvvSgdGRq/ATcrQxzM=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26/go.mod h1:3o2Wpy0bogG1kyOPrgkXA8pgIfEEv0+m19O9D5+W8y8=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.26 h1:GeNJsIFHB+WW5ap2Tec4K6dzcVTsRbsT1Lra46Hv9ME=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.26/go.mod h1:zfgMpwHDXX2WGoG84xG2H+ZlPTkJUU4YUvx2svLQYWo=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.7 h1:tB4tNw83KcajNAzaIMhkhVI2Nt8fAZd5A5ro113FEMY=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.7/go.mod h1:lvpyBGkZ3tZ9iSsUIcC2EWp+0ywa7aK3BLT+FwZi+mQ=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 h1:8eUsivBQzZHqe/3FE+cqwfH+0p5Jo8PFM/QYQSmeZ+M=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7/go.mod h1:kLPQvGUmxn/fqiCrDeohwG33bq2pQpGeY62yRO6Nrh0=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7 h1:Hi0KGbrnr57bEHWM0bJ1QcBzxLrL/k2DHvGYhb8+W1w=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7/go.mod h1:wKNgWgExdjjrm4qvfbTorkvocEstaoDl4WCvGfeCy9c=
github.com/aws/aws-sdk-go-v2/service/s3 v1.71.1 h1:aOVVZJgWbaH+EJYPvEgkNhCEbXXvH7+oML36oaPK3zE=
github.com/aws/aws-sdk-go-v2/service/s3 v1.71.1/go.mod h1:r+xl5yzMk9083rMR+sJ5TYj9Tihvf/l1oxzZXDgGj2Q=
github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 h1:CvuUmnXI7ebaUAhbJcDy9YQx8wHR69eZ9I7q5hszt/g=
github.com/aws/aws-sdk-go-v2/service/sso v1.24.8/go.mod h1:XDeGv1opzwm8ubxddF0cgqkZWsyOtw4lr6dxwmb6YQg=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 h1:F2rBfNAL5UyswqoeWv9zs74N/NanhK16ydHW1pahX6E=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7/go.mod h1:JfyQ0g2JG8+Krq0EuZNnRwX0mU0HrwY/tG6JNfcqh4k=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 h1:Xgv/hyNgvLda/M9l9qxXc4UFSgppnRczLxlMs5Ae/QY=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.3/go.mod h1:5Gn+d+VaaRgsjewpMvGazt0WfcFO+Md4wLOuBfGR9Bc=
github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro=
github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/bytedance/sonic v1.12.4 h1:9Csb3c9ZJhfUWeMtpCDCq6BUoH5ogfDFLUgQ/jG+R0k=
github.com/bytedance/sonic v1.12.4/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
@ -197,10 +231,6 @@ github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 h1:iCHtR9CQykt
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/jolestar/go-commons-pool/v2 v2.1.2 h1:E+XGo58F23t7HtZiC/W6jzO2Ux2IccSH/yx4nD+J1CM=
github.com/jolestar/go-commons-pool/v2 v2.1.2/go.mod h1:r4NYccrkS5UqP1YQI1COyTZ9UjPJAAGTUxzcsK1kqhY=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
@ -264,10 +294,10 @@ github.com/olahol/melody v1.2.1/go.mod h1:GgkTl6Y7yWj/HtfD48Q5vLKPVoZOH+Qqgfa7Cv
github.com/olekukonko/tablewriter v0.0.0-20180506121414-d4647c9c7a84/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg=
github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8=
github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc=
github.com/onsi/ginkgo/v2 v2.22.1 h1:QW7tbJAUDyVDVOM5dFa7qaybo+CRfR7bemlQUN6Z8aM=
github.com/onsi/ginkgo/v2 v2.22.1/go.mod h1:S6aTpoRsSq2cZOd+pssHAlKW/Q/jZt6cPrPlnj4a1xM=
github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw=
github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
github.com/open-spaced-repetition/go-fsrs/v3 v3.3.1 h1:zKBIfL5ZmbJfSe4nXABkazrSw7BQufi5ghXTZWXsvq8=
github.com/open-spaced-repetition/go-fsrs/v3 v3.3.1/go.mod h1:zTtQIk3kOO9kweg5zJAgbdwBXR2HBPsDN0k6AxmTpzY=
github.com/otiai10/gosseract/v2 v2.4.1 h1:G8AyBpXEeSlcq8TI85LH/pM5SXk8Djy2GEXisgyblRw=
@ -335,16 +365,16 @@ github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+D
github.com/shurcooL/gofontwoff v0.0.0-20181114050219-180f79e6909d h1:lvCTyBbr36+tqMccdGMwuEU+hjux/zL6xSmf5S9ITaA=
github.com/shurcooL/gofontwoff v0.0.0-20181114050219-180f79e6909d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
github.com/simplereach/timeutils v1.2.0/go.mod h1:VVbQDfN/FHRZa1LSqcwo4kNZ62OOyqLLGQKYB3pB0Q8=
github.com/siyuan-note/dejavu v0.0.0-20241218090338-be682e478d2e h1:7cODlg7ji5r2CnwOqKwlZxJYvoyfO94l+w2wIGjAlEg=
github.com/siyuan-note/dejavu v0.0.0-20241218090338-be682e478d2e/go.mod h1:sFK/EOHuewC6XDc/udM4eIkw/MAn0vbdL0lrfhE/xic=
github.com/siyuan-note/dejavu v0.0.0-20241220041421-b6410d912871 h1:U65kVBfF0+PYdlJ1btDxhIfuUElmz7iRbtu3yyyIo8w=
github.com/siyuan-note/dejavu v0.0.0-20241220041421-b6410d912871/go.mod h1:YtT05UDgrYO5Cb/aWc2COwqId56n6qHP+jptvITAags=
github.com/siyuan-note/encryption v0.0.0-20231219001248-1e028a4d13b4 h1:kJaw5L/evyW6LcB9IQT8PR4ppx8JVqOFP9Ix3rfwSrc=
github.com/siyuan-note/encryption v0.0.0-20231219001248-1e028a4d13b4/go.mod h1:UYcCCY+0wh+GmUoDOaO63j1sV5lgy7laLAk1XhEiUis=
github.com/siyuan-note/eventbus v0.0.0-20240627125516-396fdb0f0f97 h1:lM5v8BfNtbOL5jYwhCdMYBcYtr06IYBKjjSLAPMKTM8=
github.com/siyuan-note/eventbus v0.0.0-20240627125516-396fdb0f0f97/go.mod h1:1/nGgthl89FPA7GzAcEWKl6zRRnfgyTjzLZj9bW7kuw=
github.com/siyuan-note/filelock v0.0.0-20241212013445-c66518cdacfa h1:NM/0m8/hmFZGo0v+xNnjEeoqTtZShcVadWS0WYah+yI=
github.com/siyuan-note/filelock v0.0.0-20241212013445-c66518cdacfa/go.mod h1:OmJuq0Dm+S8I713lmfmZNFwtQ/o4mdZHSfsEra39lfY=
github.com/siyuan-note/httpclient v0.0.0-20241218090012-d3bf82b2e5be h1:79eVdjQWZgJVAQr4xHSl0xj6Y22bSd8GzKR/rUXO38o=
github.com/siyuan-note/httpclient v0.0.0-20241218090012-d3bf82b2e5be/go.mod h1:5xZ2HLlIqlNKk/dZzM0zoOs/m5m25axMUKXLazqrllc=
github.com/siyuan-note/httpclient v0.0.0-20241220030420-75c76bea8a71 h1:krZUWj9LBH990pEP7IXlSMBXloFSfhjsIvby5Blzdeo=
github.com/siyuan-note/httpclient v0.0.0-20241220030420-75c76bea8a71/go.mod h1:yNYYLSu00Ws3+DfjhA1KweS7x1SgFvTpvDxylB9wKW0=
github.com/siyuan-note/logging v0.0.0-20241218085028-6514639a9742 h1:PPiRvQDZ6bhXqvQRkOLNyNICZ/gvDuDide6gswoGsAg=
github.com/siyuan-note/logging v0.0.0-20241218085028-6514639a9742/go.mod h1:uszawhOtHHcCLZN7CZDM40J/IiwprO2lJZP4Nf4FJ14=
github.com/siyuan-note/riff v0.0.0-20241217095231-017015753318 h1:f85tgE/sYxCOvJSwmKlsVJO5NSSMsScZDJ+dhK0V+1o=
@ -450,8 +480,8 @@ golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -522,8 +552,8 @@ golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxb
golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View file

@ -961,6 +961,12 @@ func MissingAssets() (ret []string) {
}
func emojisInTree(tree *parse.Tree) (ret []string) {
if icon := tree.Root.IALAttr("icon"); "" != icon {
if !strings.Contains(icon, "://") && !strings.HasPrefix(icon, "api/icon/") {
ret = append(ret, "/emojis/"+icon)
}
}
ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
if !entering {
return ast.WalkContinue

View file

@ -875,82 +875,7 @@ func renderAttributeView(attrView *av.AttributeView, viewID, query string, page,
}
// 做一些数据兼容和订正处理,保存的时候也会做 av.SaveAttributeView()
currentTimeMillis := util.CurrentTimeMillis()
for _, kv := range attrView.KeyValues {
switch kv.Key.Type {
case av.KeyTypeBlock: // 补全 block 的创建时间和更新时间
for _, v := range kv.Values {
if 0 == v.Block.Created {
if "" == v.Block.ID {
v.Block.ID = v.BlockID
if "" == v.Block.ID {
v.Block.ID = ast.NewNodeID()
v.BlockID = v.Block.ID
}
}
createdStr := v.Block.ID[:len("20060102150405")]
created, parseErr := time.ParseInLocation("20060102150405", createdStr, time.Local)
if nil == parseErr {
v.Block.Created = created.UnixMilli()
} else {
v.Block.Created = currentTimeMillis
}
}
if 0 == v.Block.Updated {
v.Block.Updated = v.Block.Created
}
}
}
for _, v := range kv.Values {
// 校验日期 IsNotEmpty
if av.KeyTypeDate == kv.Key.Type {
if nil != v.Date && 0 != v.Date.Content && !v.Date.IsNotEmpty {
v.Date.IsNotEmpty = true
}
}
// 校验数字 IsNotEmpty
if av.KeyTypeNumber == kv.Key.Type {
if nil != v.Number && 0 != v.Number.Content && !v.Number.IsNotEmpty {
v.Number.IsNotEmpty = true
}
}
// 补全值的创建时间和更新时间
if "" == v.ID {
v.ID = ast.NewNodeID()
}
if 0 == v.CreatedAt {
createdStr := v.ID[:len("20060102150405")]
created, parseErr := time.ParseInLocation("20060102150405", createdStr, time.Local)
if nil == parseErr {
v.CreatedAt = created.UnixMilli()
} else {
v.CreatedAt = currentTimeMillis
}
}
if 0 == v.UpdatedAt {
v.UpdatedAt = v.CreatedAt
}
}
}
// 补全过滤器 Value
if nil != view.Table {
for _, f := range view.Table.Filters {
if nil != f.Value {
continue
}
if k, _ := attrView.GetKey(f.Column); nil != k {
f.Value = &av.Value{Type: k.Type}
}
}
}
upgradeAttributeViewSpec(attrView)
switch view.LayoutType {
case av.LayoutTypeTable:

View file

@ -0,0 +1,74 @@
// SiYuan - Refactor your thinking
// Copyright (c) 2020-present, b3log.org
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package model
import (
"github.com/88250/gulu"
"github.com/siyuan-note/siyuan/kernel/av"
"github.com/siyuan-note/siyuan/kernel/filesys"
"github.com/siyuan-note/siyuan/kernel/treenode"
)
func upgradeAttributeViewSpec(attrView *av.AttributeView) {
currentSpec := attrView.Spec
upgradeAttributeViewSpec1(attrView)
av.UpgradeSpec(attrView)
newSpec := attrView.Spec
if currentSpec != newSpec {
av.SaveAttributeView(attrView)
}
}
func upgradeAttributeViewSpec1(attrView *av.AttributeView) {
if 1 <= attrView.Spec {
return
}
var blockIDs []string
idBlocks := map[string]*av.Value{}
for _, kv := range attrView.KeyValues {
switch kv.Key.Type {
case av.KeyTypeBlock:
for _, v := range kv.Values {
if !v.IsDetached {
blockIDs = append(blockIDs, v.BlockID)
idBlocks[v.BlockID] = v
}
}
}
}
blockIDs = gulu.Str.RemoveDuplicatedElem(blockIDs)
trees := filesys.LoadTrees(blockIDs)
for _, id := range blockIDs {
tree := trees[id]
if nil == tree {
continue
}
node := treenode.GetNodeInTree(tree, id)
if nil == node {
continue
}
if block := idBlocks[id].Block; nil != block {
block.Icon = node.IALAttr("icon")
}
}
}

View file

@ -751,7 +751,7 @@ func ExportMarkdownHTML(id, savePath string, docx, merge bool) (name, dom string
from := filepath.Join(util.DataDir, emoji)
to := filepath.Join(savePath, emoji)
if err := filelock.Copy(from, to); err != nil {
logging.LogErrorf("copy emojis from [%s] to [%s] failed: %s", from, savePath, err)
logging.LogErrorf("copy emojis from [%s] to [%s] failed: %s", from, to, err)
return
}
}
@ -909,7 +909,7 @@ func ExportHTML(id, savePath string, pdf, image, keepFold, merge bool) (name, do
from := filepath.Join(util.DataDir, emoji)
to := filepath.Join(savePath, emoji)
if err := filelock.Copy(from, to); err != nil {
logging.LogErrorf("copy emojis from [%s] to [%s] failed: %s", from, savePath, err)
logging.LogErrorf("copy emojis from [%s] to [%s] failed: %s", from, to, err)
return
}
}
@ -1722,6 +1722,16 @@ func exportSYZip(boxID, rootDirPath, baseFolderName string, docPaths []string) (
copiedAssets.Add(asset)
}
// 复制自定义表情图片
emojis := emojisInTree(tree)
for _, emoji := range emojis {
from := filepath.Join(util.DataDir, emoji)
to := filepath.Join(exportFolder, emoji)
if copyErr := filelock.Copy(from, to); copyErr != nil {
logging.LogErrorf("copy emojis from [%s] to [%s] failed: %s", from, to, copyErr)
}
}
}
// 导出数据库 Attribute View export https://github.com/siyuan-note/siyuan/issues/8710

View file

@ -554,6 +554,25 @@ func ImportSY(zipPath, boxID, toPath string) (err error) {
os.RemoveAll(assets)
}
// 将包含的自定义表情统一移动到 data/emojis/ 下
var emojiDirs []string
filelock.Walk(unzipRootPath, func(path string, d fs.DirEntry, err error) error {
if strings.Contains(path, "emojis") && d.IsDir() {
emojiDirs = append(emojiDirs, path)
}
return nil
})
dataEmojis := filepath.Join(util.DataDir, "emojis")
for _, emojis := range emojiDirs {
if gulu.File.IsDir(emojis) {
if err = filelock.Copy(emojis, dataEmojis); err != nil {
logging.LogErrorf("copy emojis from [%s] to [%s] failed: %s", emojis, dataEmojis, err)
return
}
}
os.RemoveAll(emojis)
}
var baseTargetPath string
if "/" == toPath {
baseTargetPath = "/"

View file

@ -1016,7 +1016,11 @@ func FullTextSearchBlock(query string, boxes, paths []string, types map[string]b
typeFilter := buildTypeFilter(types)
boxFilter := buildBoxesFilter(boxes)
pathFilter := buildPathsFilter(paths)
blocks, matchedBlockCount, matchedRootCount = fullTextSearchByQuerySyntax(query, boxFilter, pathFilter, typeFilter, ignoreFilter, orderByClause, beforeLen, page, pageSize)
if ast.IsNodeIDPattern(query) {
blocks, matchedBlockCount, matchedRootCount = searchBySQL("SELECT * FROM `blocks` WHERE `id` = '"+query+"'", beforeLen, page, pageSize)
} else {
blocks, matchedBlockCount, matchedRootCount = fullTextSearchByFTS(query, boxFilter, pathFilter, typeFilter, ignoreFilter, orderByClause, beforeLen, page, pageSize)
}
case 2: // SQL
blocks, matchedBlockCount, matchedRootCount = searchBySQL(query, beforeLen, page, pageSize)
case 3: // 正则表达式
@ -1028,12 +1032,16 @@ func FullTextSearchBlock(query string, boxes, paths []string, types map[string]b
typeFilter := buildTypeFilter(types)
boxFilter := buildBoxesFilter(boxes)
pathFilter := buildPathsFilter(paths)
if 2 > len(strings.Split(strings.TrimSpace(query), " ")) {
query = stringQuery(query)
blocks, matchedBlockCount, matchedRootCount = fullTextSearchByQuerySyntax(query, boxFilter, pathFilter, typeFilter, ignoreFilter, orderByClause, beforeLen, page, pageSize)
if ast.IsNodeIDPattern(query) {
blocks, matchedBlockCount, matchedRootCount = searchBySQL("SELECT * FROM `blocks` WHERE `id` = '"+query+"'", beforeLen, page, pageSize)
} else {
docMode = true // 文档全文搜索模式 https://github.com/siyuan-note/siyuan/issues/10584
blocks, matchedBlockCount, matchedRootCount = fullTextSearchByKeyword(query, boxFilter, pathFilter, typeFilter, ignoreFilter, orderByClause, beforeLen, page, pageSize)
if 2 > len(strings.Split(strings.TrimSpace(query), " ")) {
query = stringQuery(query)
blocks, matchedBlockCount, matchedRootCount = fullTextSearchByFTS(query, boxFilter, pathFilter, typeFilter, ignoreFilter, orderByClause, beforeLen, page, pageSize)
} else {
docMode = true // 文档全文搜索模式 https://github.com/siyuan-note/siyuan/issues/10584
blocks, matchedBlockCount, matchedRootCount = fullTextSearchByLikeWithRoot(query, boxFilter, pathFilter, typeFilter, ignoreFilter, orderByClause, beforeLen, page, pageSize)
}
}
}
pageCount = (matchedBlockCount + pageSize - 1) / pageSize
@ -1367,22 +1375,6 @@ func extractID(content string) (ret string) {
return
}
func fullTextSearchByQuerySyntax(query, boxFilter, pathFilter, typeFilter, ignoreFilter, orderBy string, beforeLen, page, pageSize int) (ret []*Block, matchedBlockCount, matchedRootCount int) {
if ast.IsNodeIDPattern(query) {
ret, matchedBlockCount, matchedRootCount = searchBySQL("SELECT * FROM `blocks` WHERE `id` = '"+query+"'", beforeLen, page, pageSize)
return
}
return fullTextSearchByFTS(query, boxFilter, pathFilter, typeFilter, ignoreFilter, orderBy, beforeLen, page, pageSize)
}
func fullTextSearchByKeyword(query, boxFilter, pathFilter, typeFilter, ignoreFilter string, orderBy string, beforeLen, page, pageSize int) (ret []*Block, matchedBlockCount, matchedRootCount int) {
if ast.IsNodeIDPattern(query) {
ret, matchedBlockCount, matchedRootCount = searchBySQL("SELECT * FROM `blocks` WHERE `id` = '"+query+"'", beforeLen, page, pageSize)
return
}
return fullTextSearchByLikeWithRoot(query, boxFilter, pathFilter, typeFilter, ignoreFilter, orderBy, beforeLen, page, pageSize)
}
func fullTextSearchByRegexp(exp, boxFilter, pathFilter, typeFilter, ignoreFilter, orderBy string, beforeLen, page, pageSize int) (ret []*Block, matchedBlockCount, matchedRootCount int) {
fieldFilter := fieldRegexp(exp)
stmt := "SELECT * FROM `blocks` WHERE " + fieldFilter + " AND type IN " + typeFilter