mirror of
https://github.com/siyuan-note/siyuan
synced 2026-04-21 13:37:52 +00:00
Compare commits
32 commits
v3.6.5-dev
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
96dfe0bea4 | ||
|
|
e87fb431cc | ||
|
|
0312bb7090 | ||
|
|
71610f0109 | ||
|
|
f8cc5d5f29 | ||
|
|
b01d452d2d | ||
|
|
9f8fb571f5 | ||
|
|
5c43e082d5 | ||
|
|
3999fd33f8 | ||
|
|
e987e3fb1e | ||
|
|
f4dba03828 | ||
|
|
321abe8785 | ||
|
|
ed799763d4 | ||
|
|
237ae6ff97 | ||
|
|
464b72b24d | ||
|
|
1cb44c4d24 | ||
|
|
1995e6d421 | ||
|
|
4999feb021 | ||
|
|
43d6cb26a5 | ||
|
|
0dc545607b | ||
|
|
53bcf77106 | ||
|
|
5cedae40ba | ||
|
|
4bbcc82998 | ||
|
|
7c182042e7 | ||
|
|
59019a29c6 | ||
|
|
75a6eba371 | ||
|
|
c8a093fe1d | ||
|
|
0ddcf82eb2 | ||
|
|
e9cc256964 | ||
|
|
26681f8fb5 | ||
|
|
bfe9f5cf72 | ||
|
|
32c2c242e2 |
61 changed files with 369 additions and 221 deletions
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"hyperlink": "ارتباط تشعبي",
|
||||
"copyDoc": "نسخ النص كاملًا",
|
||||
"position": "الموقع",
|
||||
"insertColumnLeft1": "إدراج ${x} عمودًا إلى اليسار",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"hyperlink": "Hyperlink",
|
||||
"copyDoc": "Gesamten Text kopieren",
|
||||
"position": "Position",
|
||||
"insertColumnLeft1": "Füge ${x} Spalten links ein",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"hyperlink": "Hyperlink",
|
||||
"copyDoc": "Copy full document",
|
||||
"position": "Position",
|
||||
"insertColumnLeft1": "Insert ${x} column(s) to the left",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"hyperlink": "Hipervínculo",
|
||||
"copyDoc": "Copiar todo el texto",
|
||||
"position": "Posición",
|
||||
"insertColumnLeft1": "Insertar ${x} columnas a la izquierda",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"hyperlink": "Hyperlien",
|
||||
"copyDoc": "Copier tout le texte",
|
||||
"position": "Position",
|
||||
"insertColumnLeft1": "Insérer ${x} colonnes à gauche",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"hyperlink": "היפר־קישור",
|
||||
"copyDoc": "העתק את כל הטקסט",
|
||||
"position": "מיקום",
|
||||
"insertColumnLeft1": "הוסף ${x} עמודות משמאל",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"hyperlink": "Collegamento ipertestuale",
|
||||
"copyDoc": "Copia tutto il testo",
|
||||
"position": "Posizione",
|
||||
"insertColumnLeft1": "Inserisci ${x} colonne a sinistra",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"hyperlink": "ハイパーリンク",
|
||||
"copyDoc": "全文をコピー",
|
||||
"position": "位置",
|
||||
"insertColumnLeft1": "左側に ${x} 列を挿入",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"hyperlink": "하이퍼링크",
|
||||
"copyDoc": "전체 복사",
|
||||
"position": "위치",
|
||||
"insertColumnLeft1": "왼쪽에 ${x} 열 삽입",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"hyperlink": "Hiperłącze",
|
||||
"copyDoc": "Kopiuj cały tekst",
|
||||
"position": "Pozycja",
|
||||
"insertColumnLeft1": "Wstaw ${x} kolumn po lewej",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"hyperlink": "Hiperlink",
|
||||
"copyDoc": "Copiar todo o texto",
|
||||
"position": "Posição",
|
||||
"insertColumnLeft1": "Inserir ${x} colunas à esquerda",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"hyperlink": "Гиперссылка",
|
||||
"copyDoc": "Копировать весь текст",
|
||||
"position": "Позиция",
|
||||
"insertColumnLeft1": "Вставить слева ${x} столбцов",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"hyperlink": "Hypertextový odkaz",
|
||||
"copyDoc": "Kopírovať celý dokument",
|
||||
"position": "Pozícia",
|
||||
"insertColumnLeft1": "Vložiť ${x} stĺpec/stĺpce doľava",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"hyperlink": "Köprü",
|
||||
"copyDoc": "Tüm metni kopyala",
|
||||
"position": "Pozisyon",
|
||||
"insertColumnLeft1": "Sol tarafa ${x} sütun ekle",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"hyperlink": "超鏈接",
|
||||
"copyDoc": "複製全文",
|
||||
"position": "位置",
|
||||
"insertColumnLeft1": "在左側插入 ${x} 欄",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"hyperlink": "超链接",
|
||||
"copyDoc": "复制全文",
|
||||
"position": "位置",
|
||||
"insertColumnLeft1": "在左侧插入 ${x} 列",
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
<Identity Name="89C2A984.SiYuan"
|
||||
ProcessorArchitecture="arm64"
|
||||
Publisher="CN=087C656E-C1D9-42D8-8807-CED45A74FC0F"
|
||||
Version="3.6.4.0"/>
|
||||
Version="3.6.5.0"/>
|
||||
<Properties>
|
||||
<DisplayName>SiYuan</DisplayName>
|
||||
<PublisherDisplayName>云南链滴科技有限公司</PublisherDisplayName>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
<Identity Name="89C2A984.SiYuan"
|
||||
ProcessorArchitecture="x64"
|
||||
Publisher="CN=087C656E-C1D9-42D8-8807-CED45A74FC0F"
|
||||
Version="3.6.4.0"/>
|
||||
Version="3.6.5.0"/>
|
||||
<Properties>
|
||||
<DisplayName>SiYuan</DisplayName>
|
||||
<PublisherDisplayName>云南链滴科技有限公司</PublisherDisplayName>
|
||||
|
|
|
|||
|
|
@ -12,12 +12,14 @@ Below are the detailed changes in this version.
|
|||
* [Improve appearance settings for inline text on mobile](https://github.com/siyuan-note/siyuan/issues/17477)
|
||||
* [Editor toolbar does not hide when clicking outside it on mobile](https://github.com/siyuan-note/siyuan/issues/17478)
|
||||
* [Improve task list item markdown indexing for `data-task` marker](https://github.com/siyuan-note/siyuan/issues/17502)
|
||||
* [Easy way to switch between tags](https://github.com/siyuan-note/siyuan/issues/17505)
|
||||
* [Improve decoding of anchor text when pasting hyperlinks](https://github.com/siyuan-note/siyuan/issues/17513)
|
||||
* [Improve `kbd` font `--b3-font-family-kbd`](https://github.com/siyuan-note/siyuan/issues/17517)
|
||||
* [Change default Redo shortcut to `⇧⌘Z` on macOS](https://github.com/siyuan-note/siyuan/issues/17518)
|
||||
* [Improve cursor positioning after Undo in tables](https://github.com/siyuan-note/siyuan/issues/17532)
|
||||
* [Optimize code block line number rendering for better performance](https://github.com/siyuan-note/siyuan/issues/17542)
|
||||
* [Improve data indexing](https://github.com/siyuan-note/siyuan/issues/17543)
|
||||
* [Improve input method compatibility](https://github.com/siyuan-note/siyuan/issues/17546)
|
||||
* [Improve the clipping extension to resolve issues where images were too large to be clipped](https://github.com/siyuan-note/siyuan/issues/17547)
|
||||
|
||||
### Bugfix
|
||||
|
|
|
|||
|
|
@ -12,12 +12,14 @@
|
|||
* [改進行動端行級文字的外觀設定](https://github.com/siyuan-note/siyuan/issues/17477)
|
||||
* [行動端點選編輯器外部時工具列不會隱藏](https://github.com/siyuan-note/siyuan/issues/17478)
|
||||
* [改進任務清單項目中 `data-task` 標記的 Markdown 索引](https://github.com/siyuan-note/siyuan/issues/17502)
|
||||
* [改進標籤切換](https://github.com/siyuan-note/siyuan/issues/17505)
|
||||
* [改進貼上超連結時對錨文本的解碼](https://github.com/siyuan-note/siyuan/issues/17513)
|
||||
* [改良 `kbd` 字體 `--b3-font-family-kbd`](https://github.com/siyuan-note/siyuan/issues/17517)
|
||||
* [將 macOS 上預設的重做快捷鍵改為 `⇧⌘Z`](https://github.com/siyuan-note/siyuan/issues/17518)
|
||||
* [改進表格中撤銷後的遊標定位](https://github.com/siyuan-note/siyuan/issues/17532)
|
||||
* [最佳化程式碼區塊行號渲染以提升效能](https://github.com/siyuan-note/siyuan/issues/17542)
|
||||
* [改進資料索引](https://github.com/siyuan-note/siyuan/issues/17543)
|
||||
* [改進輸入法相容性](https://github.com/siyuan-note/siyuan/issues/17546)
|
||||
* [改進剪藏擴充解決圖片過大無法剪藏](https://github.com/siyuan-note/siyuan/issues/17547)
|
||||
|
||||
### 修復缺陷
|
||||
|
|
|
|||
|
|
@ -12,12 +12,14 @@
|
|||
* [改进移动端行级文本的外观设置](https://github.com/siyuan-note/siyuan/issues/17477)
|
||||
* [移动端点击编辑器外部时工具栏不会隐藏](https://github.com/siyuan-note/siyuan/issues/17478)
|
||||
* [改进任务列表项中 `data-task` 标记的 Markdown 索引](https://github.com/siyuan-note/siyuan/issues/17502)
|
||||
* [改进标签切换](https://github.com/siyuan-note/siyuan/issues/17505)
|
||||
* [改进粘贴超链接时对锚文本的解码](https://github.com/siyuan-note/siyuan/issues/17513)
|
||||
* [改进 `kbd` 字体 `--b3-font-family-kbd`](https://github.com/siyuan-note/siyuan/issues/17517)
|
||||
* [将 macOS 上默认的重做快捷键改为 `⇧⌘Z`](https://github.com/siyuan-note/siyuan/issues/17518)
|
||||
* [改进表格中撤销后的光标定位](https://github.com/siyuan-note/siyuan/issues/17532)
|
||||
* [优化代码块行号渲染以提升性能](https://github.com/siyuan-note/siyuan/issues/17542)
|
||||
* [改进数据索引](https://github.com/siyuan-note/siyuan/issues/17543)
|
||||
* [改进输入法兼容性](https://github.com/siyuan-note/siyuan/issues/17546)
|
||||
* [改进剪藏扩展解决图片过大无法剪藏](https://github.com/siyuan-note/siyuan/issues/17547)
|
||||
|
||||
### 修复缺陷
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "SiYuan",
|
||||
"version": "3.6.4",
|
||||
"version": "3.6.5",
|
||||
"description": "Refactor your thinking",
|
||||
"homepage": "https://b3log.org/siyuan",
|
||||
"main": "./electron/main.js",
|
||||
|
|
|
|||
|
|
@ -98,6 +98,9 @@ export const onGetConfig = (isStart: boolean, app: App) => {
|
|||
}
|
||||
});
|
||||
}
|
||||
window.siyuan.dialogs.forEach(item => {
|
||||
item.resize();
|
||||
});
|
||||
}, Constants.TIMEOUT_RESIZE);
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ export class Dialog {
|
|||
private disableClose: boolean;
|
||||
public editors: { [key: string]: Protyle };
|
||||
public data: any;
|
||||
private resizeCallback: (type: string) => void;
|
||||
|
||||
constructor(options: {
|
||||
positionId?: string,
|
||||
|
|
@ -29,6 +30,7 @@ export class Dialog {
|
|||
resizeCallback?: (type: string) => void,
|
||||
containerClassName?: string
|
||||
}) {
|
||||
this.resizeCallback = options.resizeCallback;
|
||||
this.disableClose = options.disableClose;
|
||||
this.id = genUUID();
|
||||
window.siyuan.dialogs.push(this);
|
||||
|
|
@ -85,6 +87,15 @@ left:${left || "auto"};top:${top || "auto"}">
|
|||
/// #endif
|
||||
}
|
||||
|
||||
public resize() {
|
||||
if (this.resizeCallback) {
|
||||
const containerElement = this.element.querySelector(".b3-dialog__container") as HTMLElement;
|
||||
if (containerElement && containerElement.style.maxWidth !== "none") {
|
||||
this.resizeCallback("l");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public destroy(options?: IObject) {
|
||||
this.element.classList.remove("b3-dialog--open");
|
||||
setTimeout(() => {
|
||||
|
|
|
|||
|
|
@ -50,14 +50,15 @@ export const reloadSync = (
|
|||
hideMessage();
|
||||
}
|
||||
/// #if MOBILE
|
||||
if (window.siyuan.mobile.popEditor) {
|
||||
if (window.siyuan.mobile.popEditor && window.siyuan.mobile.popEditor.protyle) {
|
||||
if (data.removeRootIDs.includes(window.siyuan.mobile.popEditor.protyle.block.rootID)) {
|
||||
hideElements(["dialog"]);
|
||||
} else {
|
||||
reloadProtyle(window.siyuan.mobile.popEditor.protyle, false, updateReadonly);
|
||||
}
|
||||
}
|
||||
if (window.siyuan.mobile.editor) {
|
||||
if (document.getElementById("empty").classList.contains("fn__none") &&
|
||||
window.siyuan.mobile.editor && window.siyuan.mobile.editor.protyle) {
|
||||
if (data.removeRootIDs.includes(window.siyuan.mobile.editor.protyle.block.rootID)) {
|
||||
setEmpty(app);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -5,9 +5,8 @@ import {getInstanceById, getWndByLayout, pdfIsLoading, setPanelFocus} from "../l
|
|||
import {getDockByType} from "../layout/tabUtil";
|
||||
import {getAllModels, getAllTabs} from "../layout/getAll";
|
||||
import {highlightById, scrollCenter} from "../util/highlightById";
|
||||
import {getDisplayName, useShell, pathPosix} from "../util/pathName";
|
||||
import {getDisplayName, pathPosix, useShell} from "../util/pathName";
|
||||
import {Constants} from "../constants";
|
||||
import {setEditMode} from "../protyle/util/setEditMode";
|
||||
import {Files} from "../layout/dock/Files";
|
||||
import {fetchPost, fetchSyncPost} from "../util/fetch";
|
||||
import {focusBlock, focusByOffset, focusByRange} from "../protyle/util/selection";
|
||||
|
|
|
|||
|
|
@ -993,6 +993,7 @@ export class Wnd {
|
|||
this.parent.direction = direction;
|
||||
if (direction === "tb") {
|
||||
this.parent.element.classList.add("fn__flex-column");
|
||||
this.parent.element.style.minHeight = "8px";
|
||||
this.parent.element.classList.remove("fn__flex");
|
||||
} else {
|
||||
this.parent.element.classList.remove("fn__flex-column");
|
||||
|
|
|
|||
|
|
@ -1062,7 +1062,7 @@ data-type="navigation-root" data-path="/">
|
|||
if (!fileItemElement) {
|
||||
return;
|
||||
}
|
||||
fileItemElement.setAttribute("data-name", Lute.EscapeHTMLStr(data.title));
|
||||
fileItemElement.setAttribute("data-name", data.title);
|
||||
fileItemElement.querySelector(".b3-list-item__text").innerHTML = escapeHtml(data.title);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -937,6 +937,13 @@ export const adjustLayout = (layout: Layout = window.siyuan.layout.centerLayout.
|
|||
} else {
|
||||
item.element.style.minWidth = "";
|
||||
}
|
||||
|
||||
if (!item.element.style.height && !item.element.classList.contains("layout__center") &&
|
||||
item.element.classList.contains("fn__flex-column")) {
|
||||
item.element.style.minHeight = "8px";
|
||||
} else {
|
||||
item.element.style.minHeight = "";
|
||||
}
|
||||
});
|
||||
if (layout.direction === "lr" && layout.element.scrollWidth > layout.element.clientWidth + 2) {
|
||||
let index = Math.ceil(screen.width / 8);
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ import {pushBack} from "../mobile/util/MobileBackFoward";
|
|||
import {copyPNGByLink, exportAsset, writeAssetToClipboard} from "./util";
|
||||
import {removeInlineType} from "../protyle/toolbar/util";
|
||||
import {alignImgCenter, alignImgLeft} from "../protyle/wysiwyg/commonHotkey";
|
||||
import {checkFold, renameTag} from "../util/noRelyPCFunction";
|
||||
import {checkFold, genTagList, renameTag} from "../util/noRelyPCFunction";
|
||||
import {hideElements} from "../protyle/ui/hideElements";
|
||||
import {emitOpenMenu} from "../plugin/EventBus";
|
||||
import {openMobileFileById} from "../mobile/editor";
|
||||
|
|
@ -65,6 +65,7 @@ import {hideTooltip} from "../dialog/tooltip";
|
|||
import {clearSelect} from "../protyle/util/clear";
|
||||
import {scrollCenter} from "../util/highlightById";
|
||||
import {base64ToURL} from "../util/image";
|
||||
import {setPosition} from "../util/setPosition";
|
||||
|
||||
const renderAssetList = (element: Element, k: string, position: IPosition, exts: string[] = []) => {
|
||||
fetchPost("/api/search/searchAsset", {
|
||||
|
|
@ -572,7 +573,7 @@ export const refMenu = (protyle: IProtyle, element: HTMLElement) => {
|
|||
}
|
||||
}, {
|
||||
id: "link",
|
||||
label: window.siyuan.languages.link,
|
||||
label: window.siyuan.languages.hyperlink,
|
||||
iconHTML: "",
|
||||
click() {
|
||||
element.outerHTML = `<span data-type="a" data-href="siyuan://blocks/${element.getAttribute("data-id")}">${element.innerHTML}</span><wbr>`;
|
||||
|
|
@ -1764,55 +1765,84 @@ style="margin:4px 0;width: ${isMobile() ? "100%" : "360px"}" class="b3-text-fiel
|
|||
|
||||
export const tagMenu = (protyle: IProtyle, tagElement: HTMLElement) => {
|
||||
window.siyuan.menus.menu.remove();
|
||||
window.siyuan.menus.menu.element.setAttribute("data-name", Constants.MENU_INLINE_TAG);
|
||||
const nodeElement = hasClosestBlock(tagElement);
|
||||
if (!nodeElement) {
|
||||
return;
|
||||
}
|
||||
hideElements(["util", "toolbar", "hint"], protyle);
|
||||
const id = nodeElement.getAttribute("data-node-id");
|
||||
let html = nodeElement.outerHTML;
|
||||
let inputElement: HTMLInputElement;
|
||||
const oldHTML = nodeElement.outerHTML;
|
||||
window.siyuan.menus.menu.removeCB = () => {
|
||||
tagElement.innerHTML = Constants.ZWSP + Lute.EscapeHTMLStr(inputElement.value || "");
|
||||
if (!inputElement.value) {
|
||||
tagElement.insertAdjacentHTML("afterend", "<wbr>");
|
||||
tagElement.remove();
|
||||
focusByWbr(nodeElement, protyle.toolbar.range);
|
||||
} else {
|
||||
protyle.toolbar.range.selectNodeContents(tagElement);
|
||||
protyle.toolbar.range.collapse(false);
|
||||
focusByRange(protyle.toolbar.range);
|
||||
}
|
||||
if (nodeElement.outerHTML !== oldHTML) {
|
||||
nodeElement.setAttribute("updated", dayjs().format("YYYYMMDDHHmmss"));
|
||||
updateTransaction(protyle, id, nodeElement.outerHTML, oldHTML);
|
||||
}
|
||||
};
|
||||
window.siyuan.menus.menu.element.setAttribute("data-name", Constants.MENU_INLINE_TAG);
|
||||
window.siyuan.menus.menu.append(new MenuItem({
|
||||
id: "tag",
|
||||
iconHTML: "",
|
||||
type: "readonly",
|
||||
label: `<input class="b3-text-field fn__block" style="margin: 4px 0" placeholder="${window.siyuan.languages.tag}">`,
|
||||
label: `<input class="b3-text-field fn__block" style="margin: 4px 0" placeholder="${window.siyuan.languages.tag}">
|
||||
<div class="fn__none b3-list fn__flex-1 b3-list--background protyle-hint" style="position: fixed"></div>`,
|
||||
bind(element) {
|
||||
const inputElement = element.querySelector("input");
|
||||
const listElement = element.querySelector(".b3-list") as HTMLElement;
|
||||
inputElement = element.querySelector("input");
|
||||
inputElement.value = tagElement.textContent.replace(Constants.ZWSP, "");
|
||||
inputElement.addEventListener("change", () => {
|
||||
updateTransaction(protyle, id, nodeElement.outerHTML, html);
|
||||
html = nodeElement.outerHTML;
|
||||
});
|
||||
inputElement.addEventListener("compositionend", () => {
|
||||
tagElement.innerHTML = Constants.ZWSP + Lute.EscapeHTMLStr(inputElement.value || "");
|
||||
genTagList(listElement, inputElement.value.trim());
|
||||
setPosition(listElement, inputElementRect.right + 8, inputElementRect[isMobile() ? "bottom" : "top"], inputElementRect.height);
|
||||
});
|
||||
inputElement.addEventListener("input", (event: KeyboardEvent) => {
|
||||
if (!event.isComposing) {
|
||||
// https://github.com/siyuan-note/siyuan/issues/4511
|
||||
tagElement.innerHTML = Constants.ZWSP + Lute.EscapeHTMLStr(inputElement.value || "");
|
||||
listElement.classList.remove("fn__none");
|
||||
genTagList(listElement, inputElement.value.trim());
|
||||
setPosition(listElement, inputElementRect.right + 8, inputElementRect[isMobile() ? "bottom" : "top"], inputElementRect.height);
|
||||
}
|
||||
});
|
||||
inputElement.addEventListener("keydown", (event) => {
|
||||
if ((event.key === "Enter" || event.key === "Escape") && !event.isComposing) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (!inputElement.value) {
|
||||
const oldHTML = nodeElement.outerHTML;
|
||||
tagElement.insertAdjacentHTML("afterend", "<wbr>");
|
||||
tagElement.remove();
|
||||
nodeElement.setAttribute("updated", dayjs().format("YYYYMMDDHHmmss"));
|
||||
updateTransaction(protyle, id, nodeElement.outerHTML, oldHTML);
|
||||
focusByWbr(nodeElement, protyle.toolbar.range);
|
||||
} else {
|
||||
protyle.toolbar.range.selectNodeContents(tagElement);
|
||||
protyle.toolbar.range.collapse(false);
|
||||
focusByRange(protyle.toolbar.range);
|
||||
}
|
||||
window.siyuan.menus.menu.remove();
|
||||
} else if (electronUndo(event)) {
|
||||
event.stopPropagation();
|
||||
if (event.isComposing) {
|
||||
return;
|
||||
}
|
||||
if (event.key === "Enter" || event.key === "Escape") {
|
||||
if (!listElement.classList.contains("fn__none")) {
|
||||
listElement.classList.add("fn__none");
|
||||
if (event.key === "Enter") {
|
||||
const currentElement = listElement.querySelector(".b3-list-item--focus") as HTMLElement;
|
||||
inputElement.value = currentElement.dataset.type === "new" ? currentElement.querySelector("mark").textContent.trim() : currentElement.textContent.trim();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (event.key === "Escape") {
|
||||
window.siyuan.menus.menu.removeCB = null;
|
||||
}
|
||||
window.siyuan.menus.menu.remove();
|
||||
event.preventDefault();
|
||||
} else {
|
||||
electronUndo(event);
|
||||
upDownHint(listElement, event);
|
||||
}
|
||||
});
|
||||
listElement.addEventListener("click", (event) => {
|
||||
const target = event.target as HTMLElement;
|
||||
const listItemElement = hasClosestByClassName(target, "b3-list-item");
|
||||
if (!listItemElement) {
|
||||
return;
|
||||
}
|
||||
inputElement.value = listItemElement.dataset.type === "new" ? listItemElement.querySelector("mark").textContent.trim() : listItemElement.textContent.trim();
|
||||
listElement.classList.add("fn__none");
|
||||
});
|
||||
}
|
||||
}).element);
|
||||
|
|
@ -1885,7 +1915,6 @@ export const tagMenu = (protyle: IProtyle, tagElement: HTMLElement) => {
|
|||
icon: "iconTrashcan",
|
||||
label: window.siyuan.languages.remove,
|
||||
click() {
|
||||
const oldHTML = nodeElement.outerHTML;
|
||||
tagElement.insertAdjacentHTML("afterend", "<wbr>");
|
||||
tagElement.remove();
|
||||
nodeElement.setAttribute("updated", dayjs().format("YYYYMMDDHHmmss"));
|
||||
|
|
@ -1918,7 +1947,8 @@ export const tagMenu = (protyle: IProtyle, tagElement: HTMLElement) => {
|
|||
/// #endif
|
||||
const popoverElement = hasTopClosestByClassName(protyle.element, "block__popover", true);
|
||||
window.siyuan.menus.menu.element.setAttribute("data-from", popoverElement ? popoverElement.dataset.level + "popover" : "app");
|
||||
window.siyuan.menus.menu.element.querySelector("input").select();
|
||||
inputElement.select();
|
||||
const inputElementRect = inputElement.getBoundingClientRect();
|
||||
};
|
||||
|
||||
export const inlineMathMenu = (protyle: IProtyle, element: Element) => {
|
||||
|
|
|
|||
|
|
@ -527,7 +527,7 @@ export class MobileFiles extends Model {
|
|||
if (!fileItemElement) {
|
||||
return;
|
||||
}
|
||||
fileItemElement.setAttribute("data-name", Lute.EscapeHTMLStr(data.title));
|
||||
fileItemElement.setAttribute("data-name", data.title);
|
||||
fileItemElement.querySelector(".b3-list-item__text").innerHTML = escapeHtml(data.title);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -748,7 +748,7 @@ export const initKeyboardToolbar = () => {
|
|||
preventRender = true;
|
||||
setTimeout(() => {
|
||||
preventRender = false;
|
||||
}, 1000)
|
||||
}, 1000);
|
||||
}, Constants.TIMEOUT_TRANSITION);
|
||||
}
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
/// #if !MOBILE
|
||||
import {getAllEditor, getAllModels, getAllWnds} from "../../layout/getAll";
|
||||
import {getAllModels, getAllWnds} from "../../layout/getAll";
|
||||
/// #endif
|
||||
import {addLoading} from "../ui/initUI";
|
||||
import {fetchPost} from "../../util/fetch";
|
||||
import {Constants} from "../../constants";
|
||||
import {hideAllElements, hideElements} from "../ui/hideElements";
|
||||
import {hasClosestByClassName} from "../util/hasClosest";
|
||||
import {reloadProtyle} from "../util/reload";
|
||||
import {resize} from "../util/resize";
|
||||
import {disabledProtyle, enableProtyle} from "../util/onGet";
|
||||
import {isWindow} from "../../util/functions";
|
||||
|
|
@ -20,16 +19,6 @@ export const net2LocalAssets = (protyle: IProtyle, type: "Assets" | "Img") => {
|
|||
hideElements(["toolbar"], protyle);
|
||||
fetchPost(`/api/format/net${type}2LocalAssets`, {
|
||||
id: protyle.block.rootID
|
||||
}, () => {
|
||||
/// #if MOBILE
|
||||
reloadProtyle(protyle, false);
|
||||
/// #else
|
||||
getAllEditor().forEach(item => {
|
||||
if (item.protyle.block.rootID === protyle.block.rootID) {
|
||||
reloadProtyle(item.protyle, item.protyle.element === protyle.element);
|
||||
}
|
||||
});
|
||||
/// #endif
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -111,7 +100,7 @@ export const fullscreen = (element: Element, btnElement?: Element) => {
|
|||
};
|
||||
|
||||
export const updateReadonly = (target: Element, protyle: IProtyle) => {
|
||||
if (!window.siyuan.config.readonly) {
|
||||
if (!window.siyuan.config.readonly && protyle.element.getAttribute("disabled-forever") !== "true") {
|
||||
const isReadonly = target.querySelector("use").getAttribute("xlink:href") !== "#iconUnlock";
|
||||
if (window.siyuan.config.editor.readOnly) {
|
||||
if (isReadonly) {
|
||||
|
|
|
|||
|
|
@ -573,6 +573,9 @@ ${padHTML}
|
|||
}
|
||||
|
||||
public render(protyle: IProtyle, update = false, nodeElement?: Element | false) {
|
||||
if (protyle.element.getAttribute("disabled-forever") === "true") {
|
||||
return;
|
||||
}
|
||||
/// #if !MOBILE
|
||||
let range: Range;
|
||||
let blockElement: Element;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import {Dialog} from "../../dialog";
|
|||
import {addScript} from "../util/addScript";
|
||||
import {isMobile} from "../../util/functions";
|
||||
import {Constants} from "../../constants";
|
||||
import {highlightRender} from "../render/highlightRender";
|
||||
import {highlightRender, lineNumberRender} from "../render/highlightRender";
|
||||
import {processRender} from "../util/processCode";
|
||||
import {isIPhone, isSafari, openByMobile, setStorageVal} from "../util/compatibility";
|
||||
import {useShell} from "../../util/pathName";
|
||||
|
|
@ -27,6 +27,7 @@ export const afterExport = (exportPath: string, msgId: string) => {
|
|||
|
||||
export const exportImage = (id: string) => {
|
||||
const exportDialog = new Dialog({
|
||||
disableAnimation: true,
|
||||
title: window.siyuan.languages.exportAsImage,
|
||||
content: `<div class="b3-dialog__content" style="${isMobile() ? "padding:8px;" : ""};background-color: var(--b3-theme-background)">
|
||||
<div style="${isMobile() ? "margin: 8px 0" : "padding: 48px;margin: 8px 0"}" class="export-img">
|
||||
|
|
@ -51,7 +52,14 @@ export const exportImage = (id: string) => {
|
|||
</div>
|
||||
<div class="fn__loading"><img height="128px" width="128px" src="stage/loading-pure.svg"></div>`,
|
||||
width: isMobile() ? "92vw" : "990px",
|
||||
height: "70vh"
|
||||
height: "70vh",
|
||||
resizeCallback() {
|
||||
previewElement.querySelectorAll(".code-block .protyle-linenumber__rows").forEach((item: HTMLElement) => {
|
||||
if ((item.nextElementSibling as HTMLElement).style.wordBreak === "break-word") {
|
||||
lineNumberRender(item.parentElement);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
exportDialog.element.setAttribute("data-key", Constants.DIALOG_EXPORTIMAGE);
|
||||
const btnsElement = exportDialog.element.querySelectorAll(".b3-button");
|
||||
|
|
|
|||
|
|
@ -251,7 +251,12 @@ const setHTML = (options: {
|
|||
if (protyle.breadcrumb) {
|
||||
protyle.breadcrumb.element.nextElementSibling.textContent = "";
|
||||
}
|
||||
protyle.element.removeAttribute("disabled-forever");
|
||||
if (protyle.element.hasAttribute("disabled-forever")) {
|
||||
if (protyle.wysiwyg.element.getAttribute("custom-sy-readonly") !== "true") {
|
||||
protyle.disabled = false;
|
||||
}
|
||||
protyle.element.removeAttribute("disabled-forever");
|
||||
}
|
||||
if (options.action.includes(Constants.CB_GET_OPENNEW) && window.siyuan.config.editor.readOnly && !window.siyuan.config.readonly) {
|
||||
enableProtyle(protyle);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -547,13 +547,13 @@ export const setInsertWbrHTML = (nodeElement: HTMLElement, range: Range, protyle
|
|||
const offset = getSelectionOffset(cellElement, nodeElement, range);
|
||||
cellElement.classList.add("range");
|
||||
const cloneNode = nodeElement.cloneNode(true) as HTMLElement;
|
||||
cellElement.classList.remove("range");
|
||||
cellElement.removeAttribute("class");
|
||||
const cloneCellElement = cloneNode.querySelector(".range");
|
||||
const cloneRange = focusByOffset(cloneCellElement, offset.end, offset.end, false);
|
||||
if (cloneRange) {
|
||||
cloneRange.insertNode(document.createElement("wbr"));
|
||||
}
|
||||
cloneCellElement.classList.remove("range");
|
||||
cloneCellElement.removeAttribute("class");
|
||||
protyle.wysiwyg.lastHTMLs[nodeElement.getAttribute("data-node-id")] = cloneNode.outerHTML;
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -2518,7 +2518,10 @@ export class WYSIWYG {
|
|||
input(protyle, blockElement, range, true, event);
|
||||
}, Constants.TIMEOUT_INPUT);
|
||||
} else {
|
||||
input(protyle, blockElement, range, true, event);
|
||||
clearTimeout(timeout); // https://github.com/siyuan-note/siyuan/issues/9179
|
||||
timeout = window.setTimeout(() => {
|
||||
input(protyle, blockElement, range, true, event);
|
||||
});
|
||||
}
|
||||
}
|
||||
event.stopPropagation();
|
||||
|
|
|
|||
|
|
@ -241,6 +241,7 @@ export const input = async (protyle: IProtyle, blockElement: HTMLElement, range:
|
|||
realElement = protyle.wysiwyg.element.querySelector(`[data-node-id="${tempId}"]`);
|
||||
}
|
||||
const realType = realElement.getAttribute("data-type");
|
||||
let itemHTML = "";
|
||||
if (realType === "NodeCodeBlock") {
|
||||
const languageElement = realElement.querySelector(".protyle-action__language");
|
||||
if (languageElement) {
|
||||
|
|
@ -283,6 +284,7 @@ export const input = async (protyle: IProtyle, blockElement: HTMLElement, range:
|
|||
currentWbrElement.insertAdjacentText("beforebegin", Constants.ZWSP);
|
||||
}
|
||||
}
|
||||
itemHTML = realElement.outerHTML;
|
||||
focusByWbr(protyle.wysiwyg.element, range);
|
||||
protyle.hint.render(protyle);
|
||||
// 表格出现滚动条,输入数字会向前滚 https://github.com/siyuan-note/siyuan/issues/3650
|
||||
|
|
@ -292,7 +294,7 @@ export const input = async (protyle: IProtyle, blockElement: HTMLElement, range:
|
|||
}
|
||||
}
|
||||
// https://github.com/siyuan-note/siyuan/issues/14766
|
||||
html += realElement.outerHTML;
|
||||
html += itemHTML || realElement.outerHTML;
|
||||
});
|
||||
} else if (blockElement.getAttribute("data-type") === "NodeCodeBlock") {
|
||||
editElement.parentElement.removeAttribute("data-render");
|
||||
|
|
|
|||
|
|
@ -12,6 +12,29 @@ import {upDownHint} from "./upDownHint";
|
|||
import {escapeHtml} from "./escape";
|
||||
import {hasClosestByClassName} from "../protyle/util/hasClosest";
|
||||
import {isNotCtrl} from "../protyle/util/compatibility";
|
||||
import {electronUndo} from "../protyle/undo";
|
||||
|
||||
export const genTagList = (listElement: Element, k: string) => {
|
||||
listElement.classList.remove("fn__none");
|
||||
fetchPost("/api/search/searchTag", {
|
||||
k,
|
||||
}, (response) => {
|
||||
let searchHTML = "";
|
||||
let hasKey = false;
|
||||
response.data.tags.forEach((item: string, index: number) => {
|
||||
searchHTML += `<div class="b3-list-item${index === 0 ? " b3-list-item--focus" : ""}">
|
||||
<div class="fn__flex-1">${item}</div>
|
||||
</div>`;
|
||||
if (item === `<mark>${response.data.k}</mark>`) {
|
||||
hasKey = true;
|
||||
}
|
||||
});
|
||||
if (!hasKey && response.data.k) {
|
||||
searchHTML = `<div data-type="new" class="b3-list-item${searchHTML ? "" : " b3-list-item--focus"}"><div class="fn__flex-1">${window.siyuan.languages.new} <mark>${escapeHtml(response.data.k)}</mark></div></div>` + searchHTML;
|
||||
}
|
||||
listElement.innerHTML = searchHTML;
|
||||
});
|
||||
};
|
||||
|
||||
// 需独立出来,否则移动端引用的时候会引入 pc 端大量无用代码
|
||||
export const renameTag = (labelName: string) => {
|
||||
|
|
@ -72,29 +95,18 @@ export const renameTag = (labelName: string) => {
|
|||
listElement.classList.add("fn__none");
|
||||
}
|
||||
event.preventDefault();
|
||||
} else {
|
||||
electronUndo(event);
|
||||
}
|
||||
});
|
||||
inputElement.addEventListener("input", (event) => {
|
||||
inputElement.addEventListener("input", (event: KeyboardEvent) => {
|
||||
event.stopPropagation();
|
||||
listElement.classList.remove("fn__none");
|
||||
fetchPost("/api/search/searchTag", {
|
||||
k: inputElement.value.trim(),
|
||||
}, (response) => {
|
||||
let searchHTML = "";
|
||||
let hasKey = false;
|
||||
response.data.tags.forEach((item: string, index: number) => {
|
||||
searchHTML += `<div class="b3-list-item${index === 0 ? " b3-list-item--focus" : ""}">
|
||||
<div class="fn__flex-1">${item}</div>
|
||||
</div>`;
|
||||
if (item === `<mark>${response.data.k}</mark>`) {
|
||||
hasKey = true;
|
||||
}
|
||||
});
|
||||
if (!hasKey && response.data.k) {
|
||||
searchHTML = `<div data-type="new" class="b3-list-item${searchHTML ? "" : " b3-list-item--focus"}"><div class="fn__flex-1">${window.siyuan.languages.new} <mark>${escapeHtml(response.data.k)}</mark></div></div>` + searchHTML;
|
||||
}
|
||||
listElement.innerHTML = searchHTML;
|
||||
});
|
||||
if (!event.isComposing) {
|
||||
genTagList(listElement, inputElement.value.trim());
|
||||
}
|
||||
});
|
||||
inputElement.addEventListener("compositionend", () => {
|
||||
genTagList(listElement, inputElement.value.trim());
|
||||
});
|
||||
listElement.addEventListener("click", (event) => {
|
||||
const target = event.target as HTMLElement;
|
||||
|
|
|
|||
|
|
@ -648,7 +648,7 @@ const getLeaf = (liElement: HTMLElement, flashcard: boolean) => {
|
|||
<svg class="b3-list-item__arrow"><use xlink:href="#iconRight"></use></svg>
|
||||
</span>
|
||||
${unicode2Emoji(item.icon || (item.subFileCount === 0 ? window.siyuan.storage[Constants.LOCAL_IMAGES].file : window.siyuan.storage[Constants.LOCAL_IMAGES].folder), "b3-list-item__graphic", true)}
|
||||
<span class="b3-list-item__text ariaLabel" data-position="parentE" aria-label="${getDisplayName(Lute.EscapeHTMLStr(item.name), true, true)} <small class='ft__on-surface'>${item.hSize}</small>${item.bookmark ? "<br>" + window.siyuan.languages.bookmark + " " + item.bookmark : ""}${item.name1 ? "<br>" + window.siyuan.languages.name + " " + item.name1 : ""}${item.alias ? "<br>" + window.siyuan.languages.alias + " " + item.alias : ""}${item.memo ? "<br>" + window.siyuan.languages.memo + " " + item.memo : ""}${item.subFileCount !== 0 ? window.siyuan.languages.includeSubFile.replace("x", item.subFileCount) : ""}<br>${window.siyuan.languages.modifiedAt} ${item.hMtime}<br>${window.siyuan.languages.createdAt} ${item.hCtime}">${getDisplayName(item.name, true, true)}</span>
|
||||
<span class="b3-list-item__text ariaLabel" data-position="parentE" aria-label="${getDisplayName(Lute.EscapeHTMLStr(item.name), true, true)} <small class='ft__on-surface'>${item.hSize}</small>${item.bookmark ? "<br>" + window.siyuan.languages.bookmark + " " + item.bookmark : ""}${item.name1 ? "<br>" + window.siyuan.languages.name + " " + item.name1 : ""}${item.alias ? "<br>" + window.siyuan.languages.alias + " " + item.alias : ""}${item.memo ? "<br>" + window.siyuan.languages.memo + " " + item.memo : ""}${item.subFileCount !== 0 ? window.siyuan.languages.includeSubFile.replace("x", item.subFileCount) : ""}<br>${window.siyuan.languages.modifiedAt} ${item.hMtime}<br>${window.siyuan.languages.createdAt} ${item.hCtime}">${getDisplayName(Lute.EscapeHTMLStr(item.name), true, true)}</span>
|
||||
${countHTML}
|
||||
</li>`;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -77,6 +77,9 @@ export const init = (app: App) => {
|
|||
}
|
||||
});
|
||||
}
|
||||
window.siyuan.dialogs.forEach(item => {
|
||||
item.resize();
|
||||
});
|
||||
}, Constants.TIMEOUT_RESIZE);
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -668,7 +668,15 @@ func setFollowSystemLockScreen(c *gin.Context) {
|
|||
func getSysFonts(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
ret.Data = util.LoadSysFonts()
|
||||
fonts := util.LoadSysFonts()
|
||||
|
||||
// TODO: 字重 https://github.com/siyuan-note/siyuan/issues/10313
|
||||
var families []string
|
||||
for _, font := range fonts {
|
||||
families = append(families, font.Family)
|
||||
}
|
||||
families = gulu.Str.RemoveDuplicatedElem(families)
|
||||
ret.Data = families
|
||||
}
|
||||
|
||||
func version(c *gin.Context) {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ require (
|
|||
github.com/88250/lute v1.7.7-0.20260419134724-bb68012f231d
|
||||
github.com/88250/vitess-sqlparser v0.0.0-20210205111146-56a2ded2aba1
|
||||
github.com/ClarkThan/ahocorasick v0.0.0-20231011042242-30d1ef1347f4
|
||||
github.com/ConradIrwin/font v0.2.1
|
||||
github.com/ConradIrwin/font v0.2.2-0.20260202161408-44ae4cf5fb22
|
||||
github.com/Masterminds/sprig/v3 v3.3.0
|
||||
github.com/PuerkitoBio/goquery v1.11.0
|
||||
github.com/Xuanwo/go-locale v1.1.3
|
||||
|
|
@ -78,7 +78,6 @@ require (
|
|||
github.com/vmihailenco/msgpack/v5 v5.4.1
|
||||
github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342
|
||||
github.com/xuri/excelize/v2 v2.10.1
|
||||
golang.org/x/image v0.38.0
|
||||
golang.org/x/mobile v0.0.0-20251209145715-2553ed8ce294
|
||||
golang.org/x/mod v0.34.0
|
||||
golang.org/x/net v0.53.0
|
||||
|
|
@ -138,6 +137,7 @@ require (
|
|||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.28.0 // indirect
|
||||
github.com/go-resty/resty/v2 v2.16.5 // indirect
|
||||
github.com/go-sw/text-codec v0.0.1 // indirect
|
||||
github.com/goccy/go-json v0.10.6 // indirect
|
||||
github.com/goccy/go-yaml v1.18.0 // indirect
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||
|
|
@ -194,6 +194,7 @@ require (
|
|||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
golang.org/x/arch v0.23.0 // indirect
|
||||
golang.org/x/crypto v0.50.0 // indirect
|
||||
golang.org/x/image v0.38.0 // indirect
|
||||
golang.org/x/tools v0.43.0 // indirect
|
||||
google.golang.org/protobuf v1.36.10 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@ github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk
|
|||
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/ClarkThan/ahocorasick v0.0.0-20231011042242-30d1ef1347f4 h1:r10k4+Lu1mDpiCKa1liAdGJUhB4BJHHJnMvtoli6Hts=
|
||||
github.com/ClarkThan/ahocorasick v0.0.0-20231011042242-30d1ef1347f4/go.mod h1:a3CzWIqeRxiODAscAIfZ4wbFRXxywBrdCwTENVAWB2g=
|
||||
github.com/ConradIrwin/font v0.2.1 h1:D4tWi7zyRAdVKOtOys5960HnAAfUSRx/syaf+J9JqlI=
|
||||
github.com/ConradIrwin/font v0.2.1/go.mod h1:krTLO7JWu6g8RMxG8sl+T1Hf8W93XQacBKJmqFZ2MFY=
|
||||
github.com/ConradIrwin/font v0.2.2-0.20260202161408-44ae4cf5fb22 h1:xEDrMXxOJsMByKW9Uw2WvwuVhfRd0SN5sOWVR8rYSjc=
|
||||
github.com/ConradIrwin/font v0.2.2-0.20260202161408-44ae4cf5fb22/go.mod h1:5iRYC36M+hBFrRcE25N9/kioASZaqIkAXbgOyfVDCXg=
|
||||
github.com/JalfResi/justext v0.0.0-20221106200834-be571e3e3052 h1:8T2zMbhLBbH9514PIQVHdsGhypMrsB4CxwbldKA9sBA=
|
||||
github.com/JalfResi/justext v0.0.0-20221106200834-be571e3e3052/go.mod h1:0SURuH1rsE8aVWvutuMZghRNrNrYEUzibzJfhEYR8L0=
|
||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||
|
|
@ -186,6 +186,8 @@ github.com/go-playground/validator/v10 v10.28.0/go.mod h1:GoI6I1SjPBh9p7ykNE/yj3
|
|||
github.com/go-resty/resty/v2 v2.0.0/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8=
|
||||
github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM=
|
||||
github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
|
||||
github.com/go-sw/text-codec v0.0.1 h1:H+wnKj5TuvqYlfhMtbc52m1Oe0YnxD7QPzZPBE8QAn8=
|
||||
github.com/go-sw/text-codec v0.0.1/go.mod h1:RTuIwijiCSy7wYLZd3h4RmWHBCD1bERsEST7vqnOzmo=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
|
||||
|
|
@ -529,7 +531,6 @@ golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
|||
golang.org/x/text v0.0.0-20180302201248-b7ef84aaf62a/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
|
|
|
|||
|
|
@ -227,6 +227,9 @@ func DocAssets(rootID string) (ret []string, err error) {
|
|||
}
|
||||
|
||||
func NetAssets2LocalAssets(rootID string, onlyImg bool, originalURL string) (err error) {
|
||||
syncingFiles.Store(rootID, true)
|
||||
defer syncingFiles.Delete(rootID)
|
||||
|
||||
tree, err := LoadTreeByBlockID(rootID)
|
||||
if err != nil {
|
||||
return
|
||||
|
|
@ -241,6 +244,10 @@ func NetAssets2LocalAssets(rootID string, onlyImg bool, originalURL string) (err
|
|||
}
|
||||
|
||||
err = netAssets2LocalAssets0(tree, onlyImg, originalURL, assetsDirPath, true)
|
||||
go func() {
|
||||
time.Sleep(128 * time.Microsecond)
|
||||
util.PushReloadProtyle(rootID)
|
||||
}()
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -780,7 +787,7 @@ func RemoveUnusedAssets() (ret []string) {
|
|||
|
||||
unusedAssets := UnusedAssets(false)
|
||||
|
||||
historyDir, err := GetHistoryDir(HistoryOpClean)
|
||||
historyDir, err := getHistoryDir(HistoryOpClean)
|
||||
if err != nil {
|
||||
logging.LogErrorf("get history dir failed: %s", err)
|
||||
return
|
||||
|
|
@ -850,7 +857,7 @@ func RemoveUnusedAsset(p string) (ret string) {
|
|||
return absPath
|
||||
}
|
||||
|
||||
historyDir, err := GetHistoryDir(HistoryOpClean)
|
||||
historyDir, err := getHistoryDir(HistoryOpClean)
|
||||
if err != nil {
|
||||
logging.LogErrorf("get history dir failed: %s", err)
|
||||
return
|
||||
|
|
@ -935,7 +942,11 @@ func RenameAsset(oldPath, newName string) (newPath string, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
historyDir, err := getHistoryDir(HistoryOpReplace, time.Now())
|
||||
historyDir, err := getHistoryDir(HistoryOpReplace)
|
||||
if nil != err {
|
||||
return
|
||||
}
|
||||
|
||||
luteEngine := util.NewLute()
|
||||
for _, notebook := range notebooks {
|
||||
pages := pagedPaths(filepath.Join(util.DataDir, notebook.ID), 32)
|
||||
|
|
@ -968,7 +979,7 @@ func RenameAsset(oldPath, newName string) (newPath string, err error) {
|
|||
continue
|
||||
}
|
||||
|
||||
generateTreeHistory(historyDir, tree)
|
||||
generateTreeHistory(tree, historyDir)
|
||||
treenode.UpsertBlockTree(tree)
|
||||
sql.UpsertTreeQueue(tree)
|
||||
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ func RemoveUnusedAttributeView(id string) {
|
|||
return
|
||||
}
|
||||
|
||||
historyDir, err := GetHistoryDir(HistoryOpClean)
|
||||
historyDir, err := getHistoryDir(HistoryOpClean)
|
||||
if err != nil {
|
||||
logging.LogErrorf("get history dir failed: %s", err)
|
||||
return
|
||||
|
|
@ -94,7 +94,7 @@ func RemoveUnusedAttributeViews() (ret []string) {
|
|||
|
||||
unusedAttributeViews := UnusedAttributeViews(false)
|
||||
|
||||
historyDir, err := GetHistoryDir(HistoryOpClean)
|
||||
historyDir, err := getHistoryDir(HistoryOpClean)
|
||||
if err != nil {
|
||||
logging.LogErrorf("get history dir failed: %s", err)
|
||||
return
|
||||
|
|
@ -3808,7 +3808,7 @@ func removeAttributeViewBlock(srcIDs []string, avID string, tx *Transaction) (er
|
|||
|
||||
refreshRelatedSrcAvs(avID, tx)
|
||||
|
||||
historyDir, err := GetHistoryDir(HistoryOpUpdate)
|
||||
historyDir, err := getHistoryDir(HistoryOpUpdate)
|
||||
if err != nil {
|
||||
logging.LogErrorf("get history dir failed: %s", err)
|
||||
return
|
||||
|
|
@ -3844,8 +3844,8 @@ func removeAttributeViewBlock(srcIDs []string, avID string, tx *Transaction) (er
|
|||
func removeNodeAvID(node *ast.Node, avID string, tx *Transaction, tree *parse.Tree) (err error) {
|
||||
attrs := parse.IAL2Map(node.KramdownIAL)
|
||||
if ast.NodeDocument == node.Type {
|
||||
delete(attrs, "custom-hidden")
|
||||
node.RemoveIALAttr("custom-hidden")
|
||||
delete(attrs, DocHiddenAttr)
|
||||
node.RemoveIALAttr(DocHiddenAttr)
|
||||
}
|
||||
|
||||
if avs := attrs[av.NodeAttrNameAvs]; "" != avs {
|
||||
|
|
|
|||
|
|
@ -182,8 +182,7 @@ func setNodeAttrs(node *ast.Node, tree *parse.Tree, nameValues map[string]string
|
|||
|
||||
pushBlockAttrs(oldAttrs, node)
|
||||
|
||||
if ("true" == oldAttrs["custom-hidden"] && "true" != nameValues["custom-hidden"]) ||
|
||||
"true" != oldAttrs["custom-hidden"] && "true" == nameValues["custom-hidden"] {
|
||||
if ("true" == oldAttrs[DocHiddenAttr]) != ("true" == nameValues[DocHiddenAttr]) {
|
||||
ReloadFiletree()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import (
|
|||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/88250/lute/parse"
|
||||
|
|
@ -36,6 +35,7 @@ import (
|
|||
|
||||
func RemoveBookmark(bookmark string) (err error) {
|
||||
util.PushEndlessProgress(Conf.Language(116))
|
||||
defer util.PushClearProgress()
|
||||
|
||||
bookmarks := sql.QueryBookmarkBlocks()
|
||||
treeBlocks := map[string][]string{}
|
||||
|
|
@ -47,13 +47,15 @@ func RemoveBookmark(bookmark string) (err error) {
|
|||
}
|
||||
}
|
||||
|
||||
historyDir, err := getHistoryDir(HistoryOpReplace, time.Now())
|
||||
historyDir, err := getHistoryDir(HistoryOpReplace)
|
||||
if nil != err {
|
||||
return
|
||||
}
|
||||
|
||||
for treeID, blocks := range treeBlocks {
|
||||
util.PushEndlessProgress("[" + treeID + "]")
|
||||
tree, e := LoadTreeByBlockID(treeID)
|
||||
if nil != e {
|
||||
util.PushClearProgress()
|
||||
return e
|
||||
}
|
||||
|
||||
|
|
@ -72,7 +74,7 @@ func RemoveBookmark(bookmark string) (err error) {
|
|||
}
|
||||
|
||||
if changed {
|
||||
generateTreeHistory(historyDir, tree)
|
||||
generateTreeHistory(tree, historyDir)
|
||||
util.PushEndlessProgress(fmt.Sprintf(Conf.Language(111), util.EscapeHTML(tree.Root.IALAttr("title"))))
|
||||
if err = writeTreeUpsertQueue(tree); err != nil {
|
||||
util.ClearPushProgress(100)
|
||||
|
|
@ -105,6 +107,7 @@ func RenameBookmark(oldBookmark, newBookmark string) (err error) {
|
|||
}
|
||||
|
||||
util.PushEndlessProgress(Conf.Language(110))
|
||||
defer util.ClearPushProgress(100)
|
||||
|
||||
bookmarks := sql.QueryBookmarkBlocks()
|
||||
treeBlocks := map[string][]string{}
|
||||
|
|
@ -116,12 +119,15 @@ func RenameBookmark(oldBookmark, newBookmark string) (err error) {
|
|||
}
|
||||
}
|
||||
|
||||
historyDir, err := getHistoryDir(HistoryOpReplace, time.Now())
|
||||
historyDir, err := getHistoryDir(HistoryOpReplace)
|
||||
if nil != err {
|
||||
return
|
||||
}
|
||||
|
||||
for treeID, blocks := range treeBlocks {
|
||||
util.PushEndlessProgress("[" + treeID + "]")
|
||||
tree, e := LoadTreeByBlockID(treeID)
|
||||
if nil != e {
|
||||
util.ClearPushProgress(100)
|
||||
return e
|
||||
}
|
||||
|
||||
|
|
@ -140,7 +146,7 @@ func RenameBookmark(oldBookmark, newBookmark string) (err error) {
|
|||
}
|
||||
|
||||
if changed {
|
||||
generateTreeHistory(historyDir, tree)
|
||||
generateTreeHistory(tree, historyDir)
|
||||
util.PushEndlessProgress(fmt.Sprintf(Conf.Language(111), util.EscapeHTML(tree.Root.IALAttr("title"))))
|
||||
if err = writeTreeUpsertQueue(tree); err != nil {
|
||||
util.ClearPushProgress(100)
|
||||
|
|
|
|||
|
|
@ -300,7 +300,7 @@ func ListDocTree(boxID, listPath string, sortMode int, flashcard, showHidden boo
|
|||
continue
|
||||
}
|
||||
if ial := box.docIAL(parentDocPath); nil != ial {
|
||||
if !showHidden && "true" == ial["custom-hidden"] {
|
||||
if !showHidden && "true" == ial[DocHiddenAttr] {
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -309,7 +309,7 @@ func ListDocTree(boxID, listPath string, sortMode int, flashcard, showHidden boo
|
|||
if err == nil {
|
||||
for _, subFile := range subFiles {
|
||||
subDocFilePath := path.Join(file.path, subFile.Name())
|
||||
if subIAL := box.docIAL(subDocFilePath); "true" == subIAL["custom-hidden"] {
|
||||
if subIAL := box.docIAL(subDocFilePath); "true" == subIAL[DocHiddenAttr] {
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -347,7 +347,7 @@ func ListDocTree(boxID, listPath string, sortMode int, flashcard, showHidden boo
|
|||
}
|
||||
|
||||
if ial := box.docIAL(file.path); nil != ial {
|
||||
if !showHidden && "true" == ial["custom-hidden"] {
|
||||
if !showHidden && "true" == ial[DocHiddenAttr] {
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -1090,6 +1090,7 @@ func CreateWithMarkdown(tags, boxID, hPath, md, parentID, id string, withMath bo
|
|||
const (
|
||||
DailyNoteAttrPrefix = "custom-dailynote-"
|
||||
NodeAttrTitleEmpty = "custom-sy-title-empty"
|
||||
DocHiddenAttr = "custom-hidden"
|
||||
)
|
||||
|
||||
func CreateDailyNote(boxID string) (p string, existed bool, err error) {
|
||||
|
|
@ -1564,7 +1565,7 @@ func removeDoc(box *Box, p string, luteEngine *lute.Lute) (ret *parse.Tree) {
|
|||
return
|
||||
}
|
||||
|
||||
historyDir, err := GetHistoryDir(HistoryOpDelete)
|
||||
historyDir, err := getHistoryDir(HistoryOpDelete)
|
||||
if err != nil {
|
||||
logging.LogErrorf("get history dir failed: %s", err)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -399,7 +399,7 @@ func Heading2Doc(srcHeadingID, targetBoxID, targetPath, previousPath string) (sr
|
|||
headingNode.SetIALAttr("type", "doc")
|
||||
headingNode.SetIALAttr("id", srcHeadingID)
|
||||
headingNode.SetIALAttr("title", headingText)
|
||||
headingNode.RemoveIALAttr("custom-hidden")
|
||||
headingNode.RemoveIALAttr(DocHiddenAttr)
|
||||
newTree.Root.KramdownIAL = headingNode.KramdownIAL
|
||||
|
||||
topLevel := treenode.TopHeadingLevel(newTree)
|
||||
|
|
|
|||
|
|
@ -633,7 +633,7 @@ func generateAssetsHistory() {
|
|||
return
|
||||
}
|
||||
|
||||
historyDir, err := GetHistoryDir(HistoryOpUpdate)
|
||||
historyDir, err := getHistoryDir(HistoryOpUpdate)
|
||||
if err != nil {
|
||||
logging.LogErrorf("get history dir failed: %s", err)
|
||||
return
|
||||
|
|
@ -662,7 +662,7 @@ func (box *Box) generateDocHistory0() {
|
|||
return
|
||||
}
|
||||
|
||||
historyDir, err := GetHistoryDir(HistoryOpUpdate)
|
||||
historyDir, err := getHistoryDir(HistoryOpUpdate)
|
||||
if err != nil {
|
||||
logging.LogErrorf("get history dir failed: %s", err)
|
||||
return
|
||||
|
|
@ -820,23 +820,21 @@ const (
|
|||
)
|
||||
|
||||
func generateOpTypeHistory(tree *parse.Tree, opType string) {
|
||||
historyDir, err := GetHistoryDir(opType)
|
||||
historyDir, err := getHistoryDir(opType)
|
||||
if err != nil {
|
||||
logging.LogErrorf("get history dir failed: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = generateTreeHistory(historyDir, tree); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
generateTreeHistory(tree, historyDir)
|
||||
generateAvHistoryInTree(tree, historyDir)
|
||||
|
||||
indexHistoryDir(filepath.Base(historyDir), util.NewLute())
|
||||
}
|
||||
|
||||
func generateTreeHistory(historyDir string, tree *parse.Tree) (err error) {
|
||||
func generateTreeHistory(tree *parse.Tree, historyDir string) {
|
||||
historyPath := filepath.Join(historyDir, tree.Box, tree.Path)
|
||||
var err error
|
||||
if err = os.MkdirAll(filepath.Dir(historyPath), 0755); err != nil {
|
||||
logging.LogErrorf("generate history failed: %s", err)
|
||||
return
|
||||
|
|
@ -866,12 +864,8 @@ func generateAvHistoryInTree(tree *parse.Tree, historyDir string) {
|
|||
}
|
||||
}
|
||||
|
||||
func GetHistoryDir(suffix string) (ret string, err error) {
|
||||
return getHistoryDir(suffix, time.Now())
|
||||
}
|
||||
|
||||
func getHistoryDir(suffix string, t time.Time) (ret string, err error) {
|
||||
ret = filepath.Join(util.HistoryDir, t.Format("2006-01-02-150405")+"-"+suffix)
|
||||
func getHistoryDir(suffix string) (ret string, err error) {
|
||||
ret = filepath.Join(util.HistoryDir, time.Now().Format("2006-01-02-150405")+"-"+suffix)
|
||||
if err = os.MkdirAll(ret, 0755); err != nil {
|
||||
logging.LogErrorf("make history dir failed: %s", err)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ func ListItem2Doc(srcListItemID, targetBoxID, targetPath, previousPath string) (
|
|||
listItemNode.SetIALAttr("id", srcListItemID)
|
||||
listItemNode.SetIALAttr("title", listItemText)
|
||||
listItemNode.RemoveIALAttr("fold")
|
||||
listItemNode.RemoveIALAttr("custom-hidden")
|
||||
listItemNode.RemoveIALAttr(DocHiddenAttr)
|
||||
newTree.Root.KramdownIAL = listItemNode.KramdownIAL
|
||||
srcLiParent := listItemNode.Parent
|
||||
listItemNode.Unlink()
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ func RemoveBox(boxID string) (err error) {
|
|||
|
||||
if !isUserGuide {
|
||||
var historyDir string
|
||||
historyDir, err = GetHistoryDir(HistoryOpDelete)
|
||||
historyDir, err = getHistoryDir(HistoryOpDelete)
|
||||
if err != nil {
|
||||
logging.LogErrorf("get history dir failed: %s", err)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -135,20 +135,19 @@ func refreshDocInfo0(tree *parse.Tree, size uint64) {
|
|||
}
|
||||
|
||||
subFileCount := 0
|
||||
subDir := filepath.Join(util.DataDir, tree.Box, strings.TrimSuffix(tree.Path, ".sy"))
|
||||
subFiles, err := os.ReadDir(subDir)
|
||||
if err == nil {
|
||||
for _, subFile := range subFiles {
|
||||
if "true" == tree.Root.IALAttr("custom-hidden") {
|
||||
continue
|
||||
}
|
||||
if "true" != tree.Root.IALAttr(DocHiddenAttr) {
|
||||
subDir := filepath.Join(util.DataDir, tree.Box, strings.TrimSuffix(tree.Path, ".sy"))
|
||||
subFiles, err := os.ReadDir(subDir)
|
||||
if err == nil {
|
||||
for _, subFile := range subFiles {
|
||||
if !strings.HasSuffix(subFile.Name(), ".sy") {
|
||||
continue
|
||||
}
|
||||
|
||||
subDocIAL := filesys.DocIAL(filepath.Join(subDir, subFile.Name()))
|
||||
if "true" == subDocIAL["custom-hidden"] {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasSuffix(subFile.Name(), ".sy") {
|
||||
subDocIAL := filesys.DocIAL(filepath.Join(subDir, subFile.Name()))
|
||||
if "true" == subDocIAL[DocHiddenAttr] {
|
||||
continue
|
||||
}
|
||||
subFileCount++
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -513,9 +513,8 @@ func FindReplace(keyword, replacement string, replaceTypes map[string]bool, ids
|
|||
renameRootTitles := map[string]string{}
|
||||
cachedTrees := map[string]*parse.Tree{}
|
||||
|
||||
historyDir, err := getHistoryDir(HistoryOpReplace, time.Now())
|
||||
historyDir, err := getHistoryDir(HistoryOpReplace)
|
||||
if err != nil {
|
||||
logging.LogErrorf("get history dir failed: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -543,7 +542,7 @@ func FindReplace(keyword, replacement string, replaceTypes map[string]bool, ids
|
|||
continue
|
||||
}
|
||||
|
||||
generateTreeHistory(historyDir, tree)
|
||||
generateTreeHistory(tree, historyDir)
|
||||
|
||||
cachedTrees[bt.RootID] = tree
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import (
|
|||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/88250/lute/ast"
|
||||
|
|
@ -49,7 +48,10 @@ func RemoveTag(label string) (err error) {
|
|||
|
||||
var reloadTreeIDs []string
|
||||
updateNodes := map[string]*ast.Node{}
|
||||
historyDir, err := getHistoryDir(HistoryOpReplace, time.Now())
|
||||
historyDir, err := getHistoryDir(HistoryOpReplace)
|
||||
if nil != err {
|
||||
return
|
||||
}
|
||||
|
||||
for treeID, blocks := range treeBlocks {
|
||||
util.PushEndlessProgress("[" + treeID + "]")
|
||||
|
|
@ -59,7 +61,7 @@ func RemoveTag(label string) (err error) {
|
|||
return e
|
||||
}
|
||||
|
||||
generateTreeHistory(historyDir, tree)
|
||||
generateTreeHistory(tree, historyDir)
|
||||
|
||||
var unlinks []*ast.Node
|
||||
for _, blockID := range blocks {
|
||||
|
|
@ -153,7 +155,10 @@ func RenameTag(oldLabel, newLabel string) (err error) {
|
|||
|
||||
var reloadTreeIDs []string
|
||||
updateNodes := map[string]*ast.Node{}
|
||||
historyDir, err := getHistoryDir(HistoryOpReplace, time.Now())
|
||||
historyDir, err := getHistoryDir(HistoryOpReplace)
|
||||
if nil != err {
|
||||
return
|
||||
}
|
||||
|
||||
for treeID, blocks := range treeBlocks {
|
||||
util.PushEndlessProgress("[" + treeID + "]")
|
||||
|
|
@ -163,7 +168,7 @@ func RenameTag(oldLabel, newLabel string) (err error) {
|
|||
return e
|
||||
}
|
||||
|
||||
generateTreeHistory(historyDir, tree)
|
||||
generateTreeHistory(tree, historyDir)
|
||||
|
||||
for _, blockID := range blocks {
|
||||
node := treenode.GetNodeInTree(tree, blockID)
|
||||
|
|
|
|||
|
|
@ -244,6 +244,9 @@ func LoadTreeByBlockID(id string) (ret *parse.Tree, err error) {
|
|||
func loadTreeByBlockTree(bt *treenode.BlockTree) (ret *parse.Tree, err error) {
|
||||
luteEngine := util.NewLute()
|
||||
ret, needFix, err := filesys.LoadTreeWithFix(bt.BoxID, bt.Path, luteEngine)
|
||||
if nil != err {
|
||||
return
|
||||
}
|
||||
if needFix {
|
||||
treenode.UpsertBlockTree(ret)
|
||||
sql.IndexTreeQueue(ret)
|
||||
|
|
|
|||
|
|
@ -127,13 +127,13 @@ func indexNode(tx *sql.Tx, id string) (err error) {
|
|||
return
|
||||
}
|
||||
if caseSensitive {
|
||||
stmt = "UPDATE blocks_fts_case_insensitive SET content = ? WHERE id = ?"
|
||||
stmt = "UPDATE blocks_fts SET content = ? WHERE id = ?"
|
||||
if err = execStmtTx(tx, stmt, content, id); err != nil {
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
} else {
|
||||
stmt = "UPDATE blocks_fts SET content = ? WHERE id = ?"
|
||||
stmt = "UPDATE blocks_fts_case_insensitive SET content = ? WHERE id = ?"
|
||||
if err = execStmtTx(tx, stmt, content, id); err != nil {
|
||||
tx.Rollback()
|
||||
return
|
||||
|
|
|
|||
|
|
@ -19,25 +19,24 @@ package util
|
|||
import (
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/ConradIrwin/font/sfnt"
|
||||
"github.com/flopp/go-findfont"
|
||||
"github.com/siyuan-note/logging"
|
||||
ttc "golang.org/x/image/font/sfnt"
|
||||
textUnicode "golang.org/x/text/encoding/unicode"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
var (
|
||||
sysFonts []string
|
||||
sysFonts []*Font
|
||||
sysFontsLock = sync.Mutex{}
|
||||
)
|
||||
|
||||
func LoadSysFonts() (ret []string) {
|
||||
func LoadSysFonts() []*Font {
|
||||
sysFontsLock.Lock()
|
||||
defer sysFontsLock.Unlock()
|
||||
|
||||
|
|
@ -46,21 +45,20 @@ func LoadSysFonts() (ret []string) {
|
|||
}
|
||||
|
||||
start := time.Now()
|
||||
fonts := loadFonts()
|
||||
ret = []string{}
|
||||
for _, font := range fonts {
|
||||
ret = append(ret, font.Family)
|
||||
}
|
||||
ret = gulu.Str.RemoveDuplicatedElem(ret)
|
||||
sort.Strings(ret)
|
||||
sysFonts = ret
|
||||
sysFonts = loadFonts()
|
||||
|
||||
sort.Slice(sysFonts, func(i, j int) bool {
|
||||
return sysFonts[i].DisplayName < sysFonts[j].DisplayName
|
||||
})
|
||||
|
||||
logging.LogInfof("loaded system fonts [%d] in [%dms]", len(sysFonts), time.Since(start).Milliseconds())
|
||||
return
|
||||
return sysFonts
|
||||
}
|
||||
|
||||
type Font struct {
|
||||
Path string
|
||||
Family string
|
||||
Family string `json:"family"` // 对应 CSS font-family
|
||||
Weight int `json:"weight"` // 对应 CSS font-weight
|
||||
DisplayName string `json:"displayName"` // 给人看的名称 (Family + Subfamily)
|
||||
}
|
||||
|
||||
func loadFonts() (ret []*Font) {
|
||||
|
|
@ -68,22 +66,22 @@ func loadFonts() (ret []*Font) {
|
|||
for _, fontPath := range findfont.List() {
|
||||
if strings.HasSuffix(strings.ToLower(fontPath), ".ttc") {
|
||||
families := parseTTCFontFamily(fontPath)
|
||||
for _, family := range families {
|
||||
if existFont(family, ret) {
|
||||
for _, f := range families {
|
||||
if existFont(f, ret) {
|
||||
continue
|
||||
}
|
||||
|
||||
ret = append(ret, &Font{fontPath, family})
|
||||
ret = append(ret, f)
|
||||
//LogInfof("[%s] [%s]", fontPath, family)
|
||||
}
|
||||
} else if strings.HasSuffix(strings.ToLower(fontPath), ".otf") || strings.HasSuffix(strings.ToLower(fontPath), ".ttf") {
|
||||
family := parseTTFFontFamily(fontPath)
|
||||
if "" != family {
|
||||
if existFont(family, ret) {
|
||||
f := parseTTFFontFamily(fontPath)
|
||||
if nil != f {
|
||||
if existFont(f, ret) {
|
||||
continue
|
||||
}
|
||||
|
||||
ret = append(ret, &Font{fontPath, family})
|
||||
ret = append(ret, f)
|
||||
//logging.LogInfof("[%s] [%s]", fontPath, family)
|
||||
}
|
||||
}
|
||||
|
|
@ -91,77 +89,63 @@ func loadFonts() (ret []*Font) {
|
|||
return
|
||||
}
|
||||
|
||||
func existFont(family string, fonts []*Font) bool {
|
||||
func existFont(f *Font, fonts []*Font) bool {
|
||||
for _, font := range fonts {
|
||||
if strings.EqualFold(family, font.Family) {
|
||||
if strings.EqualFold(f.Family, font.Family) && f.Weight == font.Weight {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func parseTTCFontFamily(fontPath string) (ret []string) {
|
||||
func parseTTCFontFamily(fontPath string) (ret []*Font) {
|
||||
defer logging.Recover()
|
||||
|
||||
data, err := os.ReadFile(fontPath)
|
||||
fontFile, err := os.Open(fontPath)
|
||||
if err != nil {
|
||||
//logging.LogErrorf("read font file [%s] failed: %s", fontPath, err)
|
||||
return
|
||||
}
|
||||
collection, err := ttc.ParseCollection(data)
|
||||
defer fontFile.Close()
|
||||
|
||||
fonts, err := sfnt.ParseCollection(fontFile)
|
||||
if err != nil {
|
||||
//LogErrorf("parse font collection [%s] failed: %s", fontPath, err)
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < collection.NumFonts(); i++ {
|
||||
font, err := collection.Font(i)
|
||||
if err != nil {
|
||||
//LogErrorf("get font [%s] failed: %s", fontPath, err)
|
||||
continue
|
||||
}
|
||||
|
||||
family, _ := font.Name(nil, ttc.NameIDFull)
|
||||
family = strings.TrimSpace(family)
|
||||
if "" != family && !strings.HasPrefix(family, ".") {
|
||||
ret = append(ret, family)
|
||||
}
|
||||
|
||||
family, _ = font.Name(nil, ttc.NameIDFamily)
|
||||
family = strings.TrimSpace(family)
|
||||
if "" != family && !strings.HasPrefix(family, ".") {
|
||||
ret = append(ret, family)
|
||||
}
|
||||
|
||||
family, _ = font.Name(nil, ttc.NameIDTypographicFamily)
|
||||
family = strings.TrimSpace(family)
|
||||
if "" != family && !strings.HasPrefix(family, ".") {
|
||||
ret = append(ret, family)
|
||||
for _, f := range fonts {
|
||||
font := parseFont(f)
|
||||
if nil != font {
|
||||
ret = append(ret, font)
|
||||
}
|
||||
}
|
||||
ret = gulu.Str.RemoveDuplicatedElem(ret)
|
||||
return
|
||||
}
|
||||
|
||||
func parseTTFFontFamily(fontPath string) (ret string) {
|
||||
func parseTTFFontFamily(fontPath string) *Font {
|
||||
defer logging.Recover()
|
||||
|
||||
fontFile, err := os.Open(fontPath)
|
||||
defer fontFile.Close()
|
||||
if err != nil {
|
||||
//LogErrorf("open font file [%s] failed: %s", fontPath, err)
|
||||
return
|
||||
return nil
|
||||
}
|
||||
defer fontFile.Close()
|
||||
|
||||
font, err := sfnt.Parse(fontFile)
|
||||
if err != nil {
|
||||
//LogErrorf("parse font [%s] failed: %s", fontPath, err)
|
||||
return
|
||||
//logging.LogErrorf("parse font [%s] failed: %s", fontFile.Name(), err)
|
||||
return nil
|
||||
}
|
||||
return parseFont(font)
|
||||
}
|
||||
|
||||
func parseFont(font *sfnt.Font) *Font {
|
||||
t, err := font.NameTable()
|
||||
if err != nil {
|
||||
logging.LogErrorf("get font [%s] name table failed: %s", fontPath, err)
|
||||
return
|
||||
//logging.LogErrorf("parse font name table failed: %s", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
var family, subfamily string
|
||||
|
|
@ -192,15 +176,53 @@ func parseTTFFontFamily(fontPath string) (ret string) {
|
|||
}
|
||||
}
|
||||
|
||||
//if family != "" && !strings.HasPrefix(family, ".") {
|
||||
// if subfamily != "" && !strings.Contains(subfamily, "<") && !strings.EqualFold(subfamily, "Regular") {
|
||||
// ret = family + "(" + subfamily + ")"
|
||||
// } else {
|
||||
// ret = family
|
||||
// }
|
||||
//}
|
||||
// TODO: 字重加载方案
|
||||
_ = subfamily
|
||||
ret = family
|
||||
return
|
||||
weight := 400
|
||||
os2, err := font.OS2Table()
|
||||
if nil == err {
|
||||
weight = int(os2.USWeightClass)
|
||||
}
|
||||
|
||||
if weight == 400 && subfamily != "" {
|
||||
s := strings.ToLower(subfamily)
|
||||
// 自动匹配 W01-W09
|
||||
for i := 1; i <= 9; i++ {
|
||||
wStr := "w0" + strconv.Itoa(i)
|
||||
if strings.Contains(s, wStr) {
|
||||
weight = i * 100
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 自动匹配标准关键词
|
||||
if weight == 400 { // 如果 W 系列没匹配到
|
||||
switch {
|
||||
case strings.Contains(s, "thin"):
|
||||
weight = 100
|
||||
case strings.Contains(s, "light"):
|
||||
weight = 300
|
||||
case strings.Contains(s, "medium"):
|
||||
weight = 500
|
||||
case strings.Contains(s, "semibold") || strings.Contains(s, "demi"):
|
||||
weight = 600
|
||||
case strings.Contains(s, "bold"):
|
||||
weight = 700
|
||||
case strings.Contains(s, "black") || strings.Contains(s, "heavy"):
|
||||
weight = 900
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if family != "" && !strings.HasPrefix(family, ".") {
|
||||
displayName := family
|
||||
if subfamily != "" && !strings.EqualFold(subfamily, "Regular") {
|
||||
displayName = family + " " + subfamily
|
||||
}
|
||||
|
||||
return &Font{
|
||||
Family: family,
|
||||
Weight: weight,
|
||||
DisplayName: displayName,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ import (
|
|||
var Mode = "prod"
|
||||
|
||||
const (
|
||||
Ver = "3.6.4"
|
||||
Ver = "3.6.5"
|
||||
IsInsider = false
|
||||
)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue