Merge branch 'siyuan-note:dev' into dev

This commit is contained in:
W.Kai 2024-12-10 00:17:35 +08:00 committed by GitHub
commit 531d7389a3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
64 changed files with 397 additions and 310 deletions

View file

@ -1,4 +1,5 @@
{
"removeAV": "Aus der Datenbank entfernen",
"empty": "Leer",
"newRowInRelation": "Erstellen Sie einen neuen Eintrag in ${x} <b class='ft__on-surface'>${y}</b>",
"copyKeyContent": "Primärschlüsseltext kopieren",
@ -1110,12 +1111,11 @@
"export1": "Blockzitat",
"export2": "Ankertext mit Block-URL",
"export3": "Nur Ankertext",
"export4": "Fußnoten",
"export4": "Fußnoten+Ankerhash",
"export5": "PDF-Annotation Referenz",
"export6": "Über die Verarbeitung von Ankertext in PDF-Annotationen beim Exportieren",
"export7": "Dateiname - Seitennummer - Ankertext",
"export8": "Nur Ankertext",
"export9": "Ankerhash",
"graphConfig2": "Referenzanzahlfilter",
"selectOpen": "Immer geöffnetes Dokument auswählen",
"selectOpen1": "Ausgewähltes geöffnetes Dokument",

View file

@ -1,4 +1,5 @@
{
"removeAV": "Remove from database",
"empty": "Empty",
"newRowInRelation": "Create a new entry in ${x} <b class='ft__on-surface'>${y}</b>",
"copyKeyContent": "Copy primary key text",
@ -1110,12 +1111,11 @@
"export1": "Blockquote",
"export2": "Anchor text with block URL",
"export3": "Just anchor text",
"export4": "Footnotes",
"export4": "Footnotes+Anchor hash",
"export5": "PDF Annotation Ref",
"export6": "About the handling of anchor text in PDF annotations when exporting",
"export7": "File Name - Page Number - Anchor Text",
"export8": "Just anchor text",
"export9": "Anchor hash",
"graphConfig2": "Reference Count filter",
"selectOpen": "Always Select Opened Doc",
"selectOpen1": "Select Opened Doc",

View file

@ -1,4 +1,5 @@
{
"removeAV": "Eliminar de la base de datos",
"empty": "Vacío",
"newRowInRelation": "Crear una nueva entrada en ${x} <b class='ft__on-surface'>${y}</b>",
"copyKeyContent": "Copiar texto de la clave principal",
@ -1110,12 +1111,11 @@
"export1": "Bloque de cita",
"export2": "Texto de anclaje con URL de bloque",
"export3": "Sólo texto de anclaje",
"export4": "Notas a pie de página",
"export4": "Notas a pie de página+Hash de anclaje",
"export5": "Ref. de anotación en PDF",
"export6": "Sobre el manejo del texto ancla en las anotaciones PDF al exportar",
"export7": "Nombre de archivo - Número de página - Texto ancla",
"export8": "Sólo texto ancla",
"export9": "Hash de anclaje",
"graphConfig2": "Filtro de recuento de referencias",
"selectOpen": "Seleccionar siempre el documento abierto",
"selectOpen1": "Seleccionar documento abierto",

View file

@ -1,4 +1,5 @@
{
"removeAV": "Supprimer de la base de données",
"empty": "Vide",
"newRowInRelation": "Créer une nouvelle entrée dans ${x} <b class='ft__on-surface'>${y}</b>",
"copyKeyContent": "Copier le texte de la clé principale",
@ -1110,12 +1111,11 @@
"export1": "Citation de bloc",
"export2": "Texte d'ancrage avec bloc URL",
"export3": "Juste du texte d'ancrage",
"export4": "Notes de bas de page",
"export4": "Notes de bas de page+Hash d'ancrage",
"export5": "Référence d'annotation PDF",
"export6": "À propos de la gestion du texte d'ancrage dans les annotations PDF lors de l'exportation",
"export7": "Nom de fichier - Numéro de page - Texte d'ancrage",
"export8": "Anchor text only",
"export9": "Hash d'ancrage",
"graphConfig2": "Filtre de compte de blocs de référence",
"selectOpen": "Localisez toujours les documents ouverts",
"selectOpen1": "Localiser les documents ouverts",

View file

@ -1,4 +1,5 @@
{
"removeAV": "הסר ממסד הנתונים",
"empty": "ריק",
"newRowInRelation": "צור ערך חדש ב-${x} <b class='ft__on-surface'>${y}</b>",
"copyKeyContent": "העתק טקסט מפתח ראשי",
@ -1110,12 +1111,11 @@
"export1": "ציטוט",
"export2": "טקסט עוגן עם קישור בלוק",
"export3": "רק טקסט עוגן",
"export4": ערות שוליים",
"export4": האש של העוגן+הערות שוליים",
"export5": "הערות PDF",
"export6": "על טיפולי טקסט עוגן בהערות PDF בעת הייצוא",
"export7": "שם הקובץ - מספר עמוד - טקסט עוגן",
"export8": "רק טקסט עוגן",
"export9": "ההאש של העוגן",
"graphConfig2": "מסנן ספירת הפניות",
"selectOpen": "בחר תמיד את המסמך הפתוח",
"selectOpen1": "בחר את המסמך הפתוח",

View file

@ -1,4 +1,5 @@
{
"removeAV": "Rimuovi dal database",
"empty": "Vuoto",
"newRowInRelation": "Crea una nuova voce in ${x} <b class='ft__on-surface'>${y}</b>",
"copyKeyContent": "Copia il testo della chiave primaria",
@ -1110,12 +1111,11 @@
"export1": "Citazione",
"export2": "Testo dell'ancora con URL del blocco",
"export3": "Solo testo dell'ancora",
"export4": "Note a piè di pagina",
"export4": "Note a piè di pagina+Hash dell'ancora",
"export5": "Riferimento annotazione PDF",
"export6": "Riguardo alla gestione del testo dell'ancora nelle annotazioni PDF durante l'esportazione",
"export7": "Nome file - Numero pagina - Testo dell'ancora",
"export8": "Solo testo dell'ancora",
"export9": "Hash dell'ancora",
"graphConfig2": "Filtro Conteggio Riferimenti",
"selectOpen": "Seleziona sempre il documento aperto",
"selectOpen1": "Seleziona Documento Aperto",

View file

@ -1,4 +1,5 @@
{
"removeAV": "データベースから削除",
"empty": "空白",
"newRowInRelation": "${x} に新しい項目を作成 <b class='ft__on-surface'>${y}</b>",
"copyKeyContent": "主キーのテキストをコピー",
@ -1110,12 +1111,11 @@
"export1": "ブロック参照",
"export2": "ブロック URL 付きアンカーテキスト",
"export3": "アンカーテキストのみ",
"export4": "脚注",
"export4": "脚注+アンカーハッシュ",
"export5": "PDF 注釈の参照",
"export6": "エクスポート時の PDF 注釈内のアンカーテキストの処理方法",
"export7": "ファイル名 - ページ番号 - アンカーテキスト",
"export8": "アンカーテキストのみ",
"export9": "アンカーハッシュ",
"graphConfig2": "参照カウントフィルタ",
"selectOpen": "常に開いているドキュメントを選択",
"selectOpen1": "開いているドキュメントをツリーで選択",

View file

@ -1,4 +1,5 @@
{
"removeAV": "Usuń z bazy danych",
"empty": "Pusty",
"newRowInRelation": "Utwórz nowy wpis w ${x} <b class='ft__on-surface'>${y}</b>",
"copyKeyContent": "Skopiuj tekst klucza głównego",
@ -1110,12 +1111,11 @@
"export1": "Cytat",
"export2": "Tekst kotwicy z URL bloku",
"export3": "Tylko tekst kotwicy",
"export4": "Przypisy",
"export4": "Przypisy+Hash kotwicy",
"export5": "Odnośnik do adnotacji PDF",
"export6": "O sposobie obsługi tekstu kotwicy w adnotacjach PDF podczas eksportu",
"export7": "Nazwa pliku - Numer strony - Tekst kotwicy",
"export8": "Tylko tekst kotwicy",
"export9": "Hash kotwicy",
"graphConfig2": "Filtr liczby odniesień",
"selectOpen": "Zawsze wybieraj otwarty dokument",
"selectOpen1": "Wybierz otwarty dokument",

View file

@ -1,4 +1,5 @@
{
"removeAV": "Удалить из базы данных",
"empty": "Пусто",
"newRowInRelation": "Создать новую запись в ${x} <b class='ft__on-surface'>${y}</b>",
"copyKeyContent": "Скопировать текст основного ключа",
@ -1110,12 +1111,11 @@
"export1": "Цитата",
"export2": "Текст якоря с URL блока",
"export3": "Просто текст якоря",
"export4": "Подписи",
"export4": "Подписи+Якорь хэш",
"export5": "PDF аннотация ссылка",
"export6": "О том, как обрабатывать текст якоря в PDF аннотациях при экспорте",
"export7": "Имя файла - Номер страницы - Текст якоря",
"export8": "Просто текст якоря",
"export9": "Якорь хэш",
"graphConfig2": "Фильтр по количеству ссылок",
"selectOpen": "Всегда выбирать открытый документ",
"selectOpen1": "Выбрать открытый документ",

View file

@ -1,4 +1,5 @@
{
"removeAV": "從資料庫中移除",
"empty": "空白",
"newRowInRelation": "在 ${x} 中新建條目 <b class='ft__on-surface'>${y}</b>",
"copyKeyContent": "複製主鍵文本",
@ -1110,12 +1111,11 @@
"export1": "引述塊",
"export2": "錨文字塊鏈",
"export3": "僅錨文字",
"export4": "註腳",
"export4": "註腳+錨點哈希",
"export5": "PDF 標註引用",
"export6": "導出時關於 PDF 標註引出處錨文字的處理方式",
"export7": "文件名 - 頁碼 - 錨文字",
"export8": "僅錨文字",
"export9": "錨點哈希",
"graphConfig2": "引用塊次數過濾",
"selectOpen": "定位打開的文檔",
"selectOpen1": "定位打開的文檔",

View file

@ -1,4 +1,5 @@
{
"removeAV": "从数据库中移除",
"empty": "空白",
"newRowInRelation": "在 ${x} 中新建条目 <b class='ft__on-surface'>${y}</b>",
"copyKeyContent": "复制主键文本",
@ -1110,12 +1111,11 @@
"export1": "引述块",
"export2": "锚文本块链",
"export3": "仅锚文本",
"export4": "脚注",
"export4": "脚注+锚点哈希",
"export5": "PDF 标注引用",
"export6": "导出时关于 PDF 标注引出处锚文本的处理方式",
"export7": "文件名 - 页码 - 锚文本",
"export8": "仅锚文本",
"export9": "锚点哈希",
"graphConfig2": "引用块次数过滤",
"selectOpen": "始终定位打开的文档",
"selectOpen1": "定位打开的文档",

View file

@ -109,6 +109,11 @@
--b3-color-transition: color .2s cubic-bezier(0, 0, .2, 1) 0ms;
--b3-background-transition: background 20ms ease-in 0s;
/* 高亮 */
--b3-highlight-color: #222;
--b3-highlight-background: #ffff00;
--b3-highlight-current-background: #ff9632;
/* 下拉菜单 */
--b3-select-background: url("data:image/svg+xml;utf8,<svg fill='rgba(95, 99, 104, .68)' height='24' viewBox='0 0 24 24' width='24' xmlns='http://www.w3.org/2000/svg'><path d='M7 10l5 5 5-5z'/><path d='M0 0h24v24H0z' fill='none'/></svg>") no-repeat right 2px center var(--b3-theme-background);

View file

@ -108,6 +108,11 @@
--b3-color-transition: color .2s cubic-bezier(0, 0, .2, 1) 0ms;
--b3-background-transition: background 20ms ease-in 0s;
/* 高亮 */
--b3-highlight-color: #222;
--b3-highlight-background: #ffff00;
--b3-highlight-current-background: #ff9632;
/* 下拉菜单 */
--b3-select-background: url("data:image/svg+xml;utf8,<svg fill='rgba(154, 160, 166, .68)' height='24' viewBox='0 0 24 24' width='24' xmlns='http://www.w3.org/2000/svg'><path d='M7 10l5 5 5-5z'/><path d='M0 0h24v24H0z' fill='none'/></svg>") no-repeat right 2px center var(--b3-theme-background);

View file

@ -1109,7 +1109,7 @@ app.whenReady().then(() => {
if (index === 0) {
globalShortcut.register(shortcut, () => {
let currentWorkspace;
const currentWebContentsId = (latestActiveWindow && !latestActiveWindow.isDestroyed()) ? latestActiveWindow.webContents.id : undefined
const currentWebContentsId = (latestActiveWindow && !latestActiveWindow.isDestroyed()) ? latestActiveWindow.webContents.id : undefined;
workspaces.find(workspaceItem => {
if ((currentWebContentsId || event.sender.id) === workspaceItem.browserWindow.webContents.id) {
currentWorkspace = workspaceItem;

View file

@ -392,6 +392,23 @@ const showToolbar = (element: HTMLElement, range: Range, target?: HTMLElement) =
setPosition(utilElement, targetRect.left, targetRect.top + targetRect.height + 4);
};
const getTextNode = (element: HTMLElement, isFirst: boolean) => {
const spans = element.querySelectorAll(".markedContent span");
let index = isFirst ? 0 : spans.length - 1;
while (spans[index]) {
if (spans[index].textContent) {
break;
} else {
if (isFirst) {
index++;
} else {
index--;
}
}
}
return spans[index];
};
const getHightlightCoordsByRange = (pdf: any, color: string) => {
const range = window.getSelection().getRangeAt(0);
const startPageElement = hasClosestByClassName(range.startContainer, "page");
@ -431,8 +448,7 @@ const getHightlightCoordsByRange = (pdf: any, color: string) => {
const cloneRange = range.cloneRange();
if (startIndex !== endIndex) {
const startDivs = startPage.textLayer.textDivs;
range.setEndAfter(startDivs[startDivs.length - 1]);
range.setEndAfter(getTextNode(startPage.textLayer.div, false));
}
const startSelected: number[] = [];
@ -450,8 +466,7 @@ const getHightlightCoordsByRange = (pdf: any, color: string) => {
const endPage = pdf.pdfViewer.getPageView(endIndex);
const endPageRect = endPage.canvas.getClientRects()[0];
const endViewport = endPage.viewport;
const endDivs = endPage.textLayer.textDivs;
cloneRange.setStart(endDivs[0], 0);
cloneRange.setStart(getTextNode(endPage.textLayer.div, true), 0);
mergeRects(cloneRange).forEach(function (r) {
endSelected.push(
endViewport.convertToPdfPoint(r.left - endPageRect.x,

View file

@ -25,15 +25,21 @@
}
&__avheader {
border-bottom: 1px solid var(--b3-border-color);
padding: 8px;
color: var(--b3-protyle-inline-blockref-color);
opacity: .86;
transition: var(--b3-transition);
cursor: pointer;
display: flex;
padding: 0 8px 0 32px;
align-items: center;
min-height: 42px;
&:hover {
opacity: 1;
.block__logo {
color: var(--b3-protyle-inline-blockref-color);
opacity: .86;
transition: var(--b3-transition);
flex: 1;
&:hover {
cursor: pointer;
opacity: 1;
}
}
}

View file

@ -53,70 +53,37 @@
&--secondary {
background-color: var(--b3-theme-secondary);
color: var(--b3-theme-on-secondary);
&.b3-chip--current {
box-shadow: 0 0 0 2px var(--b3-theme-secondary) inset;
}
}
&--primary {
&--primary,
&--current {
background-color: var(--b3-theme-primary);
color: var(--b3-theme-on-primary);
&.b3-chip--current {
box-shadow: 0 0 0 2px var(--b3-theme-primary) inset;
}
}
&--pink {
color: var(--b3-theme-on-secondary);
background-color: #ea4aaa;
&.b3-chip--current {
box-shadow: 0 0 0 2px #ea4aaa inset;
}
}
&--info {
color: var(--b3-card-info-color);
background-color: var(--b3-card-info-background);
&.b3-chip--current {
box-shadow: 0 0 0 2px var(--b3-card-info-background) inset;
}
}
&--warning {
color: var(--b3-card-warning-color);
background-color: var(--b3-card-warning-background);
&.b3-chip--current {
box-shadow: 0 0 0 2px var(--b3-card-warning-background) inset;
}
}
&--error {
color: var(--b3-card-error-color);
background-color: var(--b3-card-error-background);
&.b3-chip--current {
box-shadow: 0 0 0 2px var(--b3-card-error-background) inset;
}
}
&--success {
color: var(--b3-card-success-color);
background-color: var(--b3-card-success-background);
&.b3-chip--current {
box-shadow: 0 0 0 2px var(--b3-card-success-background) inset;
}
}
&--current {
background-color: var(--b3-theme-surface);
color: var(--b3-theme-on-surface);
box-shadow: 0 0 0 2px var(--b3-list-hover) inset;
}
&--pointer {

View file

@ -31,7 +31,7 @@ export const initBlockPopover = (app: App) => {
if (aElement.classList.contains("av__cell")) {
if (aElement.classList.contains("av__cell--header")) {
const textElement = aElement.querySelector(".av__celltext");
const desc = aElement.getAttribute("data-desc")
const desc = aElement.getAttribute("data-desc");
if (textElement.scrollWidth > textElement.clientWidth + 2 || desc) {
if (desc) {
tip = `${getCellText(aElement)}<div class='ft__on-surface'>${escapeAriaLabel(desc)}</div>`;
@ -56,7 +56,7 @@ export const initBlockPopover = (app: App) => {
}
} else if (aElement.parentElement.parentElement.classList.contains("av__views")) {
const textElement = aElement.querySelector(".item__text");
const desc = aElement.getAttribute("data-desc")
const desc = aElement.getAttribute("data-desc");
if (textElement.scrollWidth > textElement.clientWidth + 2 || desc) {
if (desc) {
tip = `${textElement.textContent}<div class='ft__on-surface'>${escapeAriaLabel(desc)}</div>`;

View file

@ -510,7 +510,7 @@ const editKeydown = (app: App, event: KeyboardEvent) => {
} else {
const ids = Array.from(protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select")).map(item => item.getAttribute("data-node-id"));
if (ids.length === 0) {
ids.push(nodeElement.getAttribute("data-node-id"))
ids.push(nodeElement.getAttribute("data-node-id"));
}
copyTextByType(ids, "ref");
}

View file

@ -49,7 +49,6 @@ export const exportConfig = {
<option value="2" ${window.siyuan.config.export.blockRefMode === 2 ? "selected" : ""}>${window.siyuan.languages.export2}</option>
<option value="3" ${window.siyuan.config.export.blockRefMode === 3 ? "selected" : ""}>${window.siyuan.languages.export3}</option>
<option value="4" ${window.siyuan.config.export.blockRefMode === 4 ? "selected" : ""}>${window.siyuan.languages.export4}</option>
<option value="5" ${window.siyuan.config.export.blockRefMode === 5 ? "selected" : ""}>${window.siyuan.languages.export9}</option>
</select>
</div>
<div class="fn__flex b3-label config__item">

View file

@ -179,14 +179,14 @@ export const setDefRefCount = (data: {
const attrElement = editor.protyle.title.element.querySelector(".protyle-attr");
const countElement = attrElement.querySelector(".protyle-attr--refcount");
if (countElement) {
if (data.refCount === 0) {
if (data.rootRefCount === 0) {
countElement.remove();
} else {
countElement.textContent = data.refCount.toString();
countElement.setAttribute("data-id", data.refIDs.toString());
countElement.textContent = data.rootRefCount.toString();
countElement.setAttribute("data-id", JSON.stringify(data.refIDs));
}
} else if (data.refCount > 0) {
attrElement.insertAdjacentHTML("beforeend", `<div class="protyle-attr--refcount popover__block" data-defids="[&quot;${data.blockID}&quot;]" data-id="${data.refIDs.toString()}" style="">${data.refCount}</div>`);
} else if (data.rootRefCount > 0) {
attrElement.insertAdjacentHTML("beforeend", `<div class="protyle-attr--refcount popover__block" data-defids="[&quot;${data.blockID}&quot;]" data-id="${JSON.stringify(data.refIDs)}" style="">${data.rootRefCount}</div>`);
}
return;
}

View file

@ -15,7 +15,7 @@ import {openModel} from "../mobile/menu/model";
import {closeModel} from "../mobile/util/closePanel";
import {App} from "../index";
import {resizeSide} from "./resizeSide";
import {isSupportCSSHL, searchMarkRender, searchTextMarkRender} from "../protyle/render/searchMarkRender";
import {isSupportCSSHL, searchMarkRender} from "../protyle/render/searchMarkRender";
let historyEditor: Protyle;
@ -39,6 +39,7 @@ const renderDoc = (element: HTMLElement, currentPage: number) => {
const assetElement = element.querySelector('.history__text[data-type="assetPanel"]');
const mdElement = element.querySelector('.history__text[data-type="mdPanel"]') as HTMLTextAreaElement;
const listElement = element.querySelector(".b3-list");
element.querySelector(".protyle-title__input").classList.add("fn__none");
assetElement.classList.add("fn__none");
mdElement.classList.add("fn__none");
docElement.classList.add("fn__none");
@ -399,7 +400,7 @@ export const openHistory = (app: App) => {
</ul>
<div class="history__resize"></div>
<div class="fn__flex-column fn__flex-1">
<div class="protyle-title__input ft__center ft__breakword"></div>
<div class="protyle-title__input ft__center ft__breakword fn__none"></div>
<div class="fn__flex-1 history__text fn__none" data-type="assetPanel"></div>
<textarea class="fn__flex-1 history__text fn__none" data-type="mdPanel"></textarea>
<div class="fn__flex-1 history__text fn__none" style="padding: 0" data-type="docPanel"></div>
@ -693,16 +694,16 @@ const bindEvent = (app: App, element: Element, dialog?: Dialog) => {
assetElement.classList.remove("fn__none");
assetElement.innerHTML = renderAssetsPreview(dataPath);
} else if (type === "doc") {
const k = (firstPanelElement.querySelector(".b3-text-field") as HTMLInputElement).value;
fetchPost("/api/history/getDocHistoryContent", {
historyPath: dataPath,
highlight: !isSupportCSSHL(),
k: (firstPanelElement.querySelector(".b3-text-field") as HTMLInputElement).value
k
}, (response) => {
if (response.data.isLargeDoc) {
mdElement.value = response.data.content;
mdElement.classList.remove("fn__none");
docElement.classList.add("fn__none");
searchTextMarkRender(historyEditor.protyle, ["TODO"], mdElement);
} else {
mdElement.classList.add("fn__none");
docElement.classList.remove("fn__none");
@ -712,10 +713,11 @@ const bindEvent = (app: App, element: Element, dialog?: Dialog) => {
protyle: historyEditor.protyle,
action: [Constants.CB_GET_HISTORY, Constants.CB_GET_HTML],
});
searchMarkRender(historyEditor.protyle, ["TODO"], false);
searchMarkRender(historyEditor.protyle, k.split(" "));
}
});
}
titleElement.classList.remove("fn__none");
titleElement.textContent = target.querySelector(".b3-list-item__text").textContent;
let currentItem = hasClosestByClassName(target, "b3-list") as HTMLElement;
if (currentItem) {

View file

@ -445,7 +445,7 @@ export class Wnd {
});
// 在 JSONToLayout 中进行 focus
if (!isInitActive) {
setPanelFocus(this.headersElement.parentElement.parentElement);
setPanelFocus(this.headersElement.parentElement.parentElement, isSaveLayout);
}
if (currentTab && currentTab.headElement) {
const initData = currentTab.headElement.getAttribute("data-initdata");

View file

@ -136,6 +136,14 @@ export class Backlink extends Model {
item.addEventListener("blur", (event: KeyboardEvent) => {
const inputElement = event.target as HTMLInputElement;
inputElement.classList.add("fn__none");
const filterIconElement = inputElement.nextElementSibling;
if (inputElement.value) {
filterIconElement.classList.add("block__icon--active");
filterIconElement.setAttribute("aria-label", window.siyuan.languages.filter + " " + inputElement.value);
} else {
filterIconElement.classList.remove("block__icon--active");
filterIconElement.setAttribute("aria-label", window.siyuan.languages.filter);
}
});
item.addEventListener("keydown", (event: KeyboardEvent) => {
if (!event.isComposing && event.key === "Enter") {
@ -434,11 +442,12 @@ export class Backlink extends Model {
});
svgElement.removeAttribute("disabled");
} else {
const keyword = isMention ? this.inputsElement[1].value : this.inputsElement[0].value;
fetchPost(isMention ? "/api/ref/getBackmentionDoc" : "/api/ref/getBacklinkDoc", {
defID: this.blockId,
refTreeID: docId,
highlight: !isSupportCSSHL(),
keyword: isMention ? this.inputsElement[1].value : this.inputsElement[0].value,
keyword,
}, (response) => {
svgElement.removeAttribute("disabled");
svgElement.classList.add("b3-list-item__arrow--open");
@ -458,7 +467,7 @@ export class Backlink extends Model {
}
});
editor.protyle.notebookId = liElement.getAttribute("data-notebook-id");
searchMarkRender(editor.protyle, ["TODO"], false);
searchMarkRender(editor.protyle, keyword.split(" "));
this.editors.push(editor);
});
}

View file

@ -306,6 +306,8 @@ const JSONToDock = (json: any, app: App) => {
window.siyuan.layout.bottomDock = new Dock({position: "Bottom", data: json.bottom, app});
};
const removedTabs: Tab[] = [];
export const JSONToCenter = (
app: App,
json: Config.TUILayoutItem,
@ -432,8 +434,10 @@ export const JSONToCenter = (
json.children.forEach((item: any) => {
JSONToCenter(app, item, layout ? child : window.siyuan.layout.layout);
});
} else {
} else if (json.children && Object.keys(json.children).length > 0) {
JSONToCenter(app, json.children, child);
} else if (child instanceof Tab) {
removedTabs.push(child);
}
}
};
@ -510,6 +514,10 @@ export const JSONToLayout = (app: App, isStart: boolean) => {
if (latestTabHeaderElement) {
setPanelFocus(latestTabHeaderElement.parentElement.parentElement.parentElement, false);
}
// 移除没有数据的页签 https://github.com/siyuan-note/siyuan/issues/13390
removedTabs.forEach(item => {
item.parent.removeTab(item.id);
});
}
// 需放在 tab.parent.switchTab 后,否则当前 tab 永远为最后一个
app.plugins.forEach(item => {

View file

@ -9,7 +9,7 @@ import {Constants} from "../constants";
import {openNewWindowById} from "../window/openNewWindow";
import {MenuItem} from "./Menu";
import {App} from "../index";
import {isInAndroid, isInHarmony, openByMobile, updateHotkeyTip} from "../protyle/util/compatibility";
import {isInAndroid, openByMobile, updateHotkeyTip} from "../protyle/util/compatibility";
import {checkFold} from "../util/noRelyPCFunction";
export const exportAsset = (src: string) => {
@ -175,9 +175,6 @@ export const copyPNGByLink = (link: string) => {
if (isInAndroid()) {
window.JSAndroid.writeImageClipboard(link);
return;
} else if (isInHarmony()) {
window.JSHarmony.writeImageClipboard(link);
return;
} else {
const canvas = document.createElement("canvas");
const tempElement = document.createElement("img");

View file

@ -542,7 +542,7 @@ export class Background {
this.addTags(inputElement.value, protyle);
}
inputElement.value = "";
inputElement.dispatchEvent(new CustomEvent("input"))
inputElement.dispatchEvent(new CustomEvent("input"));
} else if (event.key === "Escape") {
window.siyuan.menus.menu.remove();
}

View file

@ -28,7 +28,7 @@ import {setPanelFocus} from "../layout/util";
/// #endif
import {Title} from "./header/Title";
import {Background} from "./header/Background";
import {onGet, setReadonlyByConfig} from "./util/onGet";
import {disabledProtyle, enableProtyle, onGet, setReadonlyByConfig} from "./util/onGet";
import {reloadProtyle} from "./util/reload";
import {renderBacklink} from "./wysiwyg/renderBacklink";
import {setEmpty} from "../mobile/util/setEmpty";
@ -85,8 +85,8 @@ export class Protyle {
if (isSupportCSSHL()) {
const styleId = genUUID();
this.protyle.highlight.styleElement.dataset.uuid = styleId;
this.protyle.highlight.styleElement.textContent = `.protyle-wysiwyg::highlight(search-mark-${styleId}) {background-color: var(--b3-protyle-inline-mark-background);color: var(--b3-protyle-inline-mark-color);}
.protyle-wysiwyg::highlight(search-mark-hl-${styleId}) {background-color: var(--b3-theme-primary-lighter);box-shadow: 0 0 0 .5px var(--b3-theme-on-background);}`;
this.protyle.highlight.styleElement.textContent = `.protyle-wysiwyg::highlight(search-mark-${styleId}) {background-color: var(--b3-highlight-background);color: var(--b3-highlight-color);}
.protyle-wysiwyg::highlight(search-mark-hl-${styleId}) {color: var(--b3-highlight-color);background-color: var(--b3-highlight-current-background)}`;
}
this.protyle.hint = new Hint(this.protyle);
@ -455,4 +455,12 @@ export class Protyle {
public focusBlock(element: Element, toStart = true) {
return focusBlock(element, undefined, toStart);
}
public disable() {
disabledProtyle(this.protyle);
}
public enable() {
enableProtyle(this.protyle);
}
}

View file

@ -15,6 +15,7 @@ import {previewImage} from "../../preview/image";
import {webUtils} from "electron";
/// #endif
import {isBrowser} from "../../../util/functions";
import {Constants} from "../../../constants";
const genAVRollupHTML = (value: IAVCellValue) => {
let html = "";
@ -179,10 +180,12 @@ export const renderAVAttribute = (element: HTMLElement, id: string, protyle: IPr
avID: string
avName: string
}) => {
let innerHTML = `<div class="custom-attr__avheader block__logo popover__block" data-id='${JSON.stringify(table.blockIDs)}'>
<div class="fn__flex-1"></div>
<svg class="block__logoicon"><use xlink:href="#iconDatabase"></use></svg><span>${table.avName || window.siyuan.languages.database}</span>
<div class="fn__flex-1"></div>
let innerHTML = `<div class="custom-attr__avheader">
<div class="block__logo popover__block" data-id='${JSON.stringify(table.blockIDs)}'>
<svg class="block__logoicon"><use xlink:href="#iconDatabase"></use></svg><span>${table.avName || window.siyuan.languages.database}</span>
</div>
<span class="fn__space"></span>
<span data-type="remove" class="block__icon block__icon--show b3-tooltips__w b3-tooltips" 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}">
@ -199,7 +202,7 @@ class="fn__flex-1 fn__flex${["url", "text", "number", "email", "phone"].includes
});
innerHTML += `<div class="fn__hr"></div>
<button data-type="addColumn" class="b3-button b3-button--cancel"><svg><use xlink:href="#iconAdd"></use></svg>${window.siyuan.languages.newCol}</button>
<div class="fn__hr--b"></div>`;
<div class="fn__hr--b"></div><div class="fn__hr--b"></div>`;
html += `<div data-av-id="${table.avID}" data-node-id="${id}" data-type="NodeAttributeView">${innerHTML}</div>`;
if (element.innerHTML) {
@ -346,6 +349,28 @@ class="fn__flex-1 fn__flex${["url", "text", "number", "email", "phone"].includes
}
});
element.addEventListener("click", (event) => {
const removeElement = hasClosestByAttribute(event.target as HTMLElement, "data-type", "remove");
if (removeElement) {
const blockElement = hasClosestBlock(removeElement);
if (blockElement) {
transaction(protyle, [{
action: "removeAttrViewBlock",
srcIDs: [id],
avID: blockElement.dataset.avId,
}]);
blockElement.remove();
if (!element.innerHTML) {
window.siyuan.dialogs.find(item => {
if (item.element.getAttribute("data-key") === Constants.DIALOG_ATTR) {
item.destroy();
return true;
}
});
}
}
event.stopPropagation();
return;
}
openEdit(protyle, element, event);
});
element.addEventListener("contextmenu", (event) => {

View file

@ -385,7 +385,7 @@ export const popTextCell = (protyle: IProtyle, cellElements: HTMLElement[], type
if (cellRect.bottom > contentRect.bottom) {
height = contentRect.bottom - cellRect.top;
}
const width = Math.min(Math.max(cellRect.width, 25), contentRect.width)
const width = Math.min(Math.max(cellRect.width, 25), contentRect.width);
style = `style="padding-top: 6.5px;position:absolute;left: ${(cellRect.left < contentRect.left || cellRect.left + width > contentRect.right) ? contentRect.left : cellRect.left}px;top: ${cellRect.top}px;width:${width}px;height: ${height}px"`;
} else {
style = `style="padding-top: 6.5px;position:absolute;left: ${cellRect.left}px;top: ${cellRect.top}px;width:${Math.max(cellRect.width, 25)}px;height: ${height}px"`;

View file

@ -120,7 +120,7 @@ export const getEditHTML = (options: {
colData.options = [];
}
colData.options.forEach(item => {
const airaLabel = item.desc ? `${escapeAriaLabel(item.name)}<div class='ft__on-surface'>${escapeAriaLabel(item.desc || "")}</div>` : ""
const airaLabel = item.desc ? `${escapeAriaLabel(item.name)}<div class='ft__on-surface'>${escapeAriaLabel(item.desc || "")}</div>` : "";
html += `<button class="b3-menu__item${html ? "" : " b3-menu__item--current"}" draggable="true" data-name="${escapeAttr(item.name)}" data-desc="${escapeAttr(item.desc || "")}" data-color="${item.color}">
<svg class="b3-menu__icon fn__grab"><use xlink:href="#iconDrag"></use></svg>
<div class="fn__flex-1 ariaLabel" data-position="2parentW" aria-label="${airaLabel}">

View file

@ -29,9 +29,9 @@ export const getDateHTML = (data: IAVTable, cellElements: HTMLElement[]) => {
const currentDate = new Date().getTime();
if (cellValue?.value?.date?.isNotEmpty) {
value = dayjs(cellValue.value.date.content).format(isNotTime ? "YYYY-MM-DD" : "YYYY-MM-DD HH:mm");
const year = value.split("-")[0]
const year = value.split("-")[0];
if (year.length !== 4) {
value = new Array(4 - year.length).fill(0).join("") + value
value = new Array(4 - year.length).fill(0).join("") + value;
}
} else {
value = dayjs(currentDate).format(isNotTime ? "YYYY-MM-DD" : "YYYY-MM-DD HH:mm");
@ -39,9 +39,9 @@ export const getDateHTML = (data: IAVTable, cellElements: HTMLElement[]) => {
let value2 = "";
if (cellValue?.value?.date?.isNotEmpty2) {
value2 = dayjs(cellValue.value.date.content2).format(isNotTime ? "YYYY-MM-DD" : "YYYY-MM-DD HH:mm");
const year = value.split("-")[0]
const year = value.split("-")[0];
if (year.length !== 4) {
value = new Array(4 - year.length).fill(0).join("") + value
value = new Array(4 - year.length).fill(0).join("") + value;
}
} else if (hasEndDate) {
value2 = dayjs(currentDate).format(isNotTime ? "YYYY-MM-DD" : "YYYY-MM-DD HH:mm");
@ -144,12 +144,12 @@ export const bindDateEvent = (options: {
};
const getFullYearTime = (dateStr: string) => {
const year = dateStr.split("-")[0]
const date = new Date(dateStr)
const year = dateStr.split("-")[0];
const date = new Date(dateStr);
if (year.startsWith("00") || year.startsWith("000") || year.length < 3) {
date.setFullYear(parseInt(year))
return date.getTime()
date.setFullYear(parseInt(year));
return date.getTime();
} else {
return date.getTime()
return date.getTime();
}
}
};

View file

@ -28,7 +28,7 @@ const filterSelectHTML = (key: string, options: {
if (!key ||
(key.toLowerCase().indexOf(item.name.toLowerCase()) > -1 ||
item.name.toLowerCase().indexOf(key.toLowerCase()) > -1)) {
const airaLabel = item.desc ? `${escapeAriaLabel(item.name)}<div class='ft__on-surface'>${escapeAriaLabel(item.desc || "")}</div>` : ""
const airaLabel = item.desc ? `${escapeAriaLabel(item.name)}<div class='ft__on-surface'>${escapeAriaLabel(item.desc || "")}</div>` : "";
html += `<button data-type="addColOptionOrCell" class="b3-menu__item" data-name="${escapeAttr(item.name)}" data-desc="${escapeAttr(item.desc || "")}" draggable="true" data-color="${item.color}">
<svg class="b3-menu__icon fn__grab"><use xlink:href="#iconDrag"></use></svg>
<div class="fn__flex-1 ariaLabel" data-position="2parentW" aria-label="${airaLabel}">

View file

@ -1,6 +1,7 @@
import {Constants} from "../../constants";
import {isInEmbedBlock} from "../util/hasClosest";
export const searchMarkRender = (protyle: IProtyle, keys: string[], isHL: boolean) => {
export const searchMarkRender = (protyle: IProtyle, keys: string[], hlId?: string | number, cb?: () => void) => {
if (!isSupportCSSHL()) {
return;
}
@ -8,6 +9,16 @@ export const searchMarkRender = (protyle: IProtyle, keys: string[], isHL: boolea
protyle.highlight.markHL.clear();
protyle.highlight.mark.clear();
protyle.highlight.ranges = [];
let isSetHL = false;
let hlBlockElement: Element;
if (typeof hlId === "string") {
Array.from(protyle.wysiwyg.element.querySelectorAll(`[data-node-id='${hlId}']`)).find(item => {
if (!isInEmbedBlock(item)) {
hlBlockElement = item;
return true;
}
});
}
// 准备一个数组来保存所有文本节点
@ -19,15 +30,18 @@ export const searchMarkRender = (protyle: IProtyle, keys: string[], isHL: boolea
let currentNode = treeWalker.nextNode();
while (currentNode) {
textNodes.push(currentNode);
currentSize += currentNode.textContent.length
currentSize += currentNode.textContent.length;
textNodesSize.push(currentSize);
currentNode = treeWalker.nextNode();
}
const text = protyle.wysiwyg.element.textContent;
const rangeIndexes: { range: Range, startIndex: number }[] = [];
const rangeIndexes: { range: Range, startIndex: number, isCurrent: boolean }[] = [];
keys.forEach(key => {
if (!key) {
return;
}
let startIndex = 0;
let endIndex = 0;
let currentNodeIndex = 0;
@ -42,27 +56,34 @@ export const searchMarkRender = (protyle: IProtyle, keys: string[], isHL: boolea
range.setStart(currentTextNode, startIndex - (currentNodeIndex ? textNodesSize[currentNodeIndex - 1] : 0));
while (currentNodeIndex < textNodes.length && textNodesSize[currentNodeIndex] < endIndex) {
currentNodeIndex++
currentNodeIndex++;
}
currentTextNode = textNodes[currentNodeIndex]
currentTextNode = textNodes[currentNodeIndex];
range.setEnd(currentTextNode, endIndex - (currentNodeIndex ? textNodesSize[currentNodeIndex - 1] : 0));
rangeIndexes.push({range, startIndex});
let isCurrent = false;
if (!isSetHL && hlBlockElement && hlBlockElement.contains(currentTextNode)) {
isSetHL = true;
isCurrent = true;
}
rangeIndexes.push({range, startIndex, isCurrent});
} catch (e) {
console.error("searchMarkRender error:", e);
}
startIndex = endIndex;
}
})
});
rangeIndexes.sort((b, a) => {
if (a.startIndex > b.startIndex) {
return -1
return -1;
} else {
return 0
return 0;
}
}).forEach((item, index) => {
if (index === protyle.highlight.rangeIndex && isHL) {
if ((typeof hlId === "string" && item.isCurrent) || (typeof hlId === "number" && hlId === index)) {
protyle.highlight.rangeIndex = index;
protyle.highlight.markHL.add(item.range);
} else {
protyle.highlight.mark.add(item.range);
@ -70,78 +91,15 @@ export const searchMarkRender = (protyle: IProtyle, keys: string[], isHL: boolea
protyle.highlight.ranges.push(item.range);
});
CSS.highlights.set("search-mark-" + protyle.highlight.styleElement.dataset.uuid, protyle.highlight.mark);
if (isHL) {
if (typeof hlId !== "undefined") {
CSS.highlights.set("search-mark-hl-" + protyle.highlight.styleElement.dataset.uuid, protyle.highlight.markHL);
}
if (cb) {
cb();
}
}, protyle.wysiwyg.element.querySelector(".hljs") ? Constants.TIMEOUT_TRANSITION : 0);
};
export const searchTextMarkRender = (protyle: IProtyle, keys: string[], element: HTMLElement,) => {
if (!isSupportCSSHL()) {
return;
}
protyle.highlight.markHL.clear();
protyle.highlight.mark.clear();
// 准备一个数组来保存所有文本节点
const textNodes: Node[] = [];
const textNodesSize: number[] = [];
let currentSize = 0;
const treeWalker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT);
let currentNode = treeWalker.nextNode();
while (currentNode) {
textNodes.push(currentNode);
currentSize += currentNode.textContent.length
textNodesSize.push(currentSize);
currentNode = treeWalker.nextNode();
}
const text = element.textContent;
const rangeIndexes: { range: Range, startIndex: number }[] = [];
keys.forEach(key => {
let startIndex = 0;
let endIndex = 0;
let currentNodeIndex = 0;
while ((startIndex = text.indexOf(key, startIndex)) !== -1) {
const range = new Range();
endIndex = startIndex + key.length;
try {
while (currentNodeIndex < textNodes.length && textNodesSize[currentNodeIndex] <= startIndex) {
currentNodeIndex++;
}
let currentTextNode = textNodes[currentNodeIndex];
range.setStart(currentTextNode, startIndex - (currentNodeIndex ? textNodesSize[currentNodeIndex - 1] : 0));
while (currentNodeIndex < textNodes.length && textNodesSize[currentNodeIndex] < endIndex) {
currentNodeIndex++
}
currentTextNode = textNodes[currentNodeIndex]
range.setEnd(currentTextNode, endIndex - (currentNodeIndex ? textNodesSize[currentNodeIndex - 1] : 0));
rangeIndexes.push({range, startIndex});
} catch (e) {
console.error("searchMarkRender error:", e);
}
startIndex = endIndex;
}
})
rangeIndexes.sort((b, a) => {
if (a.startIndex > b.startIndex) {
return -1
} else {
return 0
}
}).forEach((item, index) => {
protyle.highlight.mark.add(item.range);
});
CSS.highlights.set("search-mark-" + protyle.highlight.styleElement.dataset.uuid, protyle.highlight.mark);
}
export const isSupportCSSHL = () => {
return !!(CSS && CSS.highlights);
}
};

View file

@ -53,7 +53,7 @@ export const getDocByScroll = (options: {
protyle: IProtyle,
scrollAttr?: IScrollAttr,
mergedOptions?: IProtyleOptions,
cb?: () => void
cb?: (keys: string[]) => void
focus?: boolean,
updateReadonly?: boolean
}) => {
@ -89,7 +89,9 @@ export const getDocByScroll = (options: {
protyle: options.protyle,
action: actions,
scrollAttr: options.scrollAttr,
afterCB: options.cb,
afterCB: options.cb ? () => {
options.cb(response.data.keywords);
} : undefined,
updateReadonly: options.updateReadonly
});
});
@ -100,7 +102,9 @@ export const getDocByScroll = (options: {
protyle: options.protyle,
action: actions,
scrollAttr: options.scrollAttr,
afterCB: options.cb,
afterCB: options.cb ? () => {
options.cb(response.data.keywords);
} : undefined,
updateReadonly: options.updateReadonly
});
}
@ -121,7 +125,9 @@ export const getDocByScroll = (options: {
protyle: options.protyle,
action: actions,
scrollAttr: options.scrollAttr,
afterCB: options.cb,
afterCB: options.cb ? () => {
options.cb(response.data.keywords);
} : undefined,
updateReadonly: options.updateReadonly
});
});

View file

@ -415,7 +415,7 @@ export const insertHTML = (html: string, protyle: IProtyle, isBlock = false,
(insertBefore ? Array.from(tempElement.content.children) : Array.from(tempElement.content.children).reverse()).forEach((item) => {
// https://github.com/siyuan-note/siyuan/issues/13232
if (item.getAttribute("data-type") === "NodeHeading" && item.getAttribute("fold") === "1") {
item.removeAttribute("fold")
item.removeAttribute("fold");
}
let addId = item.getAttribute("data-node-id");
if (addId === id) {

View file

@ -37,15 +37,16 @@ export const reloadProtyle = (protyle: IProtyle, focus: boolean, updateReadonly?
const tabElement = hasClosestByClassName(protyle.element, "sy__backlink");
if (tabElement) {
const inputsElement = tabElement.querySelectorAll(".b3-text-field") as NodeListOf<HTMLInputElement>;
const keyword = isMention ? inputsElement[1].value : inputsElement[0].value;
fetchPost(isMention ? "/api/ref/getBackmentionDoc" : "/api/ref/getBacklinkDoc", {
defID: protyle.element.getAttribute("data-defid"),
refTreeID: protyle.block.rootID,
highlight: !isSupportCSSHL(),
keyword: isMention ? inputsElement[1].value : inputsElement[0].value,
keyword,
}, response => {
protyle.options.backlinkData = isMention ? response.data.backmentions : response.data.backlinks;
renderBacklink(protyle, protyle.options.backlinkData);
searchMarkRender(protyle, ["TODO"], false);
searchMarkRender(protyle, keyword.split(" "));
});
}
} else {
@ -55,9 +56,9 @@ export const reloadProtyle = (protyle: IProtyle, focus: boolean, updateReadonly?
focus,
scrollAttr: saveScroll(protyle, true) as IScrollAttr,
updateReadonly,
cb() {
cb(keys) {
if (protyle.query?.key) {
searchMarkRender(protyle, ["TODO"], true);
searchMarkRender(protyle, keys, protyle.highlight.rangeIndex);
}
}
});

View file

@ -743,4 +743,4 @@ export const clearTableCell = (protyle: IProtyle, tableBlockElement: HTMLElement
item.innerHTML = "";
});
updateTransaction(protyle, tableBlockElement.getAttribute("data-node-id"), tableBlockElement.outerHTML, oldHTML);
}
};

View file

@ -1298,7 +1298,7 @@ export class WYSIWYG {
icon: "iconTrashcan",
accelerator: "⌦",
click() {
clearTableCell(protyle, tableBlockElement as HTMLElement)
clearTableCell(protyle, tableBlockElement as HTMLElement);
}
}).element);
window.siyuan.menus.menu.append(new MenuItem({

View file

@ -1311,12 +1311,9 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => {
// toolbar action
if (matchHotKey(window.siyuan.config.keymap.editor.insert.lastUsed.custom, event)) {
protyle.toolbar.range = range;
let selectElements: Element[] = [];
if (selectText === "" && protyle.toolbar.getCurrentType(range).length === 0) {
selectElements = Array.from(protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select"));
if (selectElements.length === 0) {
selectElements = [nodeElement];
}
const selectElements: Element[] = Array.from(protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select"));
if (selectText === "" && selectElements.length === 0) {
selectElements.push(nodeElement);
}
fontEvent(protyle, selectElements);
event.stopPropagation();

View file

@ -289,7 +289,7 @@ const saveCriterionData = (config: Config.IUILayoutTabSearchConfig,
const criteriaElement = element.querySelector("#criteria").firstElementChild;
criteriaElement.classList.remove("fn__none");
criteriaElement.querySelector(".b3-chip--current")?.classList.remove("b3-chip--current");
criteriaElement.insertAdjacentHTML("beforeend", `<div data-type="set-criteria" class="b3-chip b3-chip--current b3-chip--middle b3-chip--pointer b3-chip--${["secondary", "primary", "info", "success", "warning", "error", ""][(criteriaElement.childElementCount) % 7]}">${criterion.name}<svg class="b3-chip__close" data-type="remove-criteria"><use xlink:href="#iconCloseRound"></use></svg></div>`);
criteriaElement.insertAdjacentHTML("beforeend", `<div data-type="set-criteria" class="b3-chip b3-chip--current b3-chip--middle b3-chip--pointer">${criterion.name}<svg class="b3-chip__close" data-type="remove-criteria"><use xlink:href="#iconCloseRound"></use></svg></div>`);
});
};
@ -630,7 +630,7 @@ export const initCriteriaMenu = (element: HTMLElement, data: Config.IUILayoutTab
if (configIsSame(item, config)) {
isSame = true;
}
html += `<div data-type="set-criteria" class="${isSame ? "b3-chip--current " : ""}b3-chip b3-chip--middle b3-chip--pointer b3-chip--${["secondary", "primary", "info", "success", "warning", "error", ""][index % 7]}">${escapeHtml(item.name)}<svg class="b3-chip__close" data-type="remove-criteria"><use xlink:href="#iconCloseRound"></use></svg></div>`;
html += `<div data-type="set-criteria" class="${isSame ? "b3-chip--current " : ""}b3-chip b3-chip--middle b3-chip--pointer">${escapeHtml(item.name)}<svg class="b3-chip__close" data-type="remove-criteria"><use xlink:href="#iconCloseRound"></use></svg></div>`;
});
/// #if MOBILE
element.innerHTML = `<div class="b3-chips${html?"":" fn__none"}">

View file

@ -1200,18 +1200,27 @@ const renderNextSearchMark = (options: {
}
};
let articleId: string;
export const getArticle = (options: {
id: string,
config?: Config.IUILayoutTabSearchConfig,
edit: Protyle
value?: string,
}) => {
articleId = options.id;
checkFold(options.id, (zoomIn) => {
if (articleId !== options.id) {
return;
}
options.edit.protyle.scroll.lastScrollTop = 0;
addLoading(options.edit.protyle);
fetchPost("/api/block/getDocInfo", {
id: options.id,
}, (response) => {
if (articleId !== options.id) {
return;
}
options.edit.protyle.wysiwyg.renderCustom(response.data.ial);
fetchPost("/api/filetree/getDoc", {
id: options.id,
@ -1223,6 +1232,9 @@ export const getArticle = (options: {
zoom: zoomIn,
highlight: !isSupportCSSHL(),
}, getResponse => {
if (articleId !== options.id) {
return;
}
options.edit.protyle.query = {
key: options.value || null,
method: options.config?.method || null,
@ -1236,20 +1248,20 @@ export const getArticle = (options: {
});
const contentRect = options.edit.protyle.contentElement.getBoundingClientRect();
let matchRectTop: number;
if (isSupportCSSHL()) {
options.edit.protyle.highlight.rangeIndex = 0;
searchMarkRender(options.edit.protyle, ["TODO", "得到"], true);
matchRectTop = options.edit.protyle.highlight.ranges[0].getBoundingClientRect().top;
searchMarkRender(options.edit.protyle, getResponse.data.keywords, options.id, () => {
if (options.edit.protyle.highlight.ranges.length > 0 && options.edit.protyle.highlight.ranges[options.edit.protyle.highlight.rangeIndex]) {
options.edit.protyle.contentElement.scrollTop = options.edit.protyle.contentElement.scrollTop + options.edit.protyle.highlight.ranges[options.edit.protyle.highlight.rangeIndex].getBoundingClientRect().top - contentRect.top - contentRect.height / 2;
}
});
} else {
const matchElements = options.edit.protyle.wysiwyg.element.querySelectorAll('span[data-type~="search-mark"]');
if (matchElements.length === 0) {
return;
}
matchElements[0].classList.add("search-mark--hl");
matchRectTop = matchElements[0].getBoundingClientRect().top;
options.edit.protyle.contentElement.scrollTop = options.edit.protyle.contentElement.scrollTop + matchElements[0].getBoundingClientRect().top - contentRect.top - contentRect.height / 2;
}
options.edit.protyle.contentElement.scrollTop = options.edit.protyle.contentElement.scrollTop + matchRectTop - contentRect.top - contentRect.height / 2;
});
});
});

View file

@ -268,7 +268,18 @@ declare namespace Config {
* User interface language
* Same as {@link IAppearance.lang}
*/
export type TLang = "en_US" | "es_ES" | "fr_FR" | "zh_CHT" | "zh_CN" | "ja_JP" | "it_IT" | "de_DE" | "he_IL" | "ru_RU" | "pl_PL";
export type TLang =
"en_US"
| "es_ES"
| "fr_FR"
| "zh_CHT"
| "zh_CN"
| "ja_JP"
| "it_IT"
| "de_DE"
| "he_IL"
| "ru_RU"
| "pl_PL";
/**
* SiYuan bazaar related configuration

View file

@ -198,9 +198,7 @@ interface Window {
changeStatusBarColor(color: string, mode: number): void
writeClipboard(text: string): void
writeHTMLClipboard(text: string, html: string): void
writeImageClipboard(uri: string): void
readClipboard(): string
getBlockURL(): string
}
Protyle: import("../protyle/method").default

View file

@ -484,7 +484,7 @@ interface IProtyle {
mark: Highlight
markHL: Highlight
ranges: Range[]
rangeIndex: 0
rangeIndex: number
styleElement: HTMLStyleElement
}
getInstance: () => import("../protyle").Protyle,

View file

@ -344,7 +344,7 @@ const updateMobileTheme = (OSTheme: string) => {
window.webkit.messageHandlers.changeStatusBar.postMessage((backgroundColor || (mode === 0 ? "#fff" : "#1e1e1e")) + " " + mode);
} else if (isInAndroid()) {
window.JSAndroid.changeStatusBarColor(backgroundColor, mode);
} else if ( isInHarmony()) {
} else if (isInHarmony()) {
window.JSHarmony.changeStatusBarColor(backgroundColor, mode);
}
}, 500); // 移动端需要加载完才可以获取到颜色

View file

@ -412,7 +412,7 @@ func getRefIDs(c *gin.Context) {
}
id := arg["id"].(string)
refIDs, refTexts, defIDs := model.GetBlockRefs(id)
refIDs, refTexts, defIDs := model.GetBlockRefs(id, true)
ret.Data = map[string][]string{
"refIDs": refIDs,
"refTexts": refTexts,

View file

@ -1152,6 +1152,7 @@ func getDoc(c *gin.Context) {
"isSyncing": isSyncing,
"isBacklinkExpand": isBacklinkExpand,
"keywords": keywords,
"reqId": arg["reqId"],
}
}

View file

@ -66,6 +66,7 @@ func ServeAPI(ginServer *gin.Engine) {
ginServer.Handle("POST", "/api/system/exportConf", model.CheckAuth, model.CheckAdminRole, exportConf)
ginServer.Handle("POST", "/api/system/importConf", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, importConf)
ginServer.Handle("POST", "/api/system/getWorkspaceInfo", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, getWorkspaceInfo)
ginServer.Handle("POST", "/api/system/reloadUI", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, reloadUI)
ginServer.Handle("POST", "/api/storage/setLocalStorage", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, setLocalStorage)
ginServer.Handle("POST", "/api/storage/getLocalStorage", model.CheckAuth, getLocalStorage)

View file

@ -35,6 +35,13 @@ import (
"github.com/siyuan-note/siyuan/kernel/util"
)
func reloadUI(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
util.ReloadUI()
}
func getWorkspaceInfo(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)

View file

@ -371,6 +371,7 @@ func (value *Value) GetValByType(typ KeyType) (ret interface{}) {
type ValueBlock struct {
ID string `json:"id"`
Icon string `json:"icon"`
Content string `json:"content"`
Created int64 `json:"created"`
Updated int64 `json:"updated"`

View file

@ -22,9 +22,9 @@ type Export struct {
// 内容块引用导出模式
// 2锚文本块链
// 3仅锚文本
// 4块引转脚注
// 5锚点哈希 https://github.com/siyuan-note/siyuan/issues/10265
// 0使用原始文本1使用 Blockquote。0 和 1 都已经废弃 https://github.com/siyuan-note/siyuan/issues/3155
// 4块引转脚注+锚点哈希
// 5锚点哈希 https://github.com/siyuan-note/siyuan/issues/10265 已经废弃 https://github.com/siyuan-note/siyuan/issues/13331
// 0使用原始文本1使用 Blockquote都已经废弃 https://github.com/siyuan-note/siyuan/issues/3155
BlockRefMode int `json:"blockRefMode"`
BlockEmbedMode int `json:"blockEmbedMode"` // 内容块引用导出模式0使用原始文本1使用 Blockquote
BlockRefTextLeft string `json:"blockRefTextLeft"` // 内容块引用导出锚文本左侧符号,默认留空

View file

@ -10,7 +10,7 @@ require (
github.com/88250/epub v0.0.0-20230830085737-c19055cd1f48
github.com/88250/go-humanize v0.0.0-20240424102817-4f78fac47ea7
github.com/88250/gulu v1.2.3-0.20241127120230-1ae6a9868a2d
github.com/88250/lute v1.7.7-0.20241206153537-408cb4f3fbab
github.com/88250/lute v1.7.7-0.20241208103455-3223b6b5f502
github.com/88250/vitess-sqlparser v0.0.0-20210205111146-56a2ded2aba1
github.com/ClarkThan/ahocorasick v0.0.0-20231011042242-30d1ef1347f4
github.com/ConradIrwin/font v0.0.0-20240627033111-8567075b2bfe

View file

@ -14,8 +14,8 @@ github.com/88250/go-sqlite3 v1.14.13-0.20231214121541-e7f54c482950 h1:Pa5hMiBceT
github.com/88250/go-sqlite3 v1.14.13-0.20231214121541-e7f54c482950/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/88250/gulu v1.2.3-0.20241127120230-1ae6a9868a2d h1:dexFyk3UkR4c2xpyrC4Zk4L28xFbfLYAeowIW/7QYEA=
github.com/88250/gulu v1.2.3-0.20241127120230-1ae6a9868a2d/go.mod h1:MUfzyfmbPrRDZLqxc7aPrVYveatTHRfoUa5TynPS0i8=
github.com/88250/lute v1.7.7-0.20241206153537-408cb4f3fbab h1:TRN5UZIude8Gmks6ODC6bpT6MklMhBCDwjclpsX0YgQ=
github.com/88250/lute v1.7.7-0.20241206153537-408cb4f3fbab/go.mod h1:VDAzL8b+oCh+e3NAlmwwLzC53ten0rZlS8NboB7ljtk=
github.com/88250/lute v1.7.7-0.20241208103455-3223b6b5f502 h1:agB9kKKmVrmMa4iSpI7YAN4O3OyV3W+w09CTwgnY7Z8=
github.com/88250/lute v1.7.7-0.20241208103455-3223b6b5f502/go.mod h1:VDAzL8b+oCh+e3NAlmwwLzC53ten0rZlS8NboB7ljtk=
github.com/88250/pdfcpu v0.3.14-0.20241201033812-5a93b7586a01 h1:AcFe63RXjIh1XtX/dc4Es3U8bYKjlEkvavHd1nFBOHM=
github.com/88250/pdfcpu v0.3.14-0.20241201033812-5a93b7586a01/go.mod h1:fVfOloBzs2+W2VJCCbq60XIxc3yJHAZ0Gahv1oO0gyI=
github.com/88250/vitess-sqlparser v0.0.0-20210205111146-56a2ded2aba1 h1:48T899JQDwyyRu9yXHePYlPdHtpJfrJEUGBMH3SMBWY=

View file

@ -1980,8 +1980,9 @@ func addAttributeViewBlock(now int64, avID, blockID, previousBlockID, addingBloc
return
}
var blockIcon string
if !isDetached {
addingBlockContent = getNodeAvBlockText(node)
blockIcon, addingBlockContent = getNodeAvBlockText(node)
}
// 检查是否重复添加相同的块
@ -1992,6 +1993,7 @@ func addAttributeViewBlock(now int64, avID, blockID, previousBlockID, addingBloc
// 重复绑定一下,比如剪切数据库块、取消绑定块后再次添加的场景需要
bindBlockAv0(tx, avID, node, tree)
blockValue.IsDetached = isDetached
blockValue.Block.Icon = blockIcon
blockValue.Block.Content = addingBlockContent
blockValue.UpdatedAt = now
err = av.SaveAttributeView(attrView)
@ -2936,7 +2938,7 @@ func replaceAttributeViewBlock(operation *Operation, tx *Transaction) (err error
if !operation.IsDetached {
bindBlockAv0(tx, operation.AvID, node, tree)
value.IsDetached = false
value.Block.Content = getNodeAvBlockText(node)
value.Block.Icon, value.Block.Content = getNodeAvBlockText(node)
value.UpdatedAt = now
err = av.SaveAttributeView(attrView)
}
@ -2973,7 +2975,7 @@ func replaceAttributeViewBlock(operation *Operation, tx *Transaction) (err error
value.Block.ID = operation.NextID
value.IsDetached = operation.IsDetached
if !operation.IsDetached {
value.Block.Content = getNodeAvBlockText(node)
value.Block.Icon, value.Block.Content = getNodeAvBlockText(node)
}
}

View file

@ -90,7 +90,8 @@ func GetDocInfo(blockID string) (ret *BlockInfo) {
}
}
ret.RefIDs, _ = sql.QueryRefIDsByDefID(blockID, false)
ret.RefIDs, _ = sql.QueryRefIDsByDefID(blockID, Conf.Editor.BacklinkContainChildren)
buildBacklinkListItemRefs(&ret.RefIDs)
ret.RefCount = len(ret.RefIDs) // 填充块引计数
// 填充属性视图角标 Display the database title on the block superscript https://github.com/siyuan-note/siyuan/issues/10545
@ -162,7 +163,7 @@ func GetDocsInfo(blockIDs []string, queryRefCount bool, queryAv bool) (rets []*B
}
}
if queryRefCount {
ret.RefIDs, _ = sql.QueryRefIDsByDefID(blockID, false)
ret.RefIDs, _ = sql.QueryRefIDsByDefID(blockID, Conf.Editor.BacklinkContainChildren)
ret.RefCount = len(ret.RefIDs) // 填充块引计数
}
@ -267,22 +268,19 @@ func getNodeRefText(node *ast.Node) string {
return getNodeRefText0(node, Conf.Editor.BlockRefDynamicAnchorTextMaxLen)
}
func getNodeAvBlockText(node *ast.Node) (ret string) {
func getNodeAvBlockText(node *ast.Node) (icon, content string) {
if nil == node {
return ""
return
}
icon = node.IALAttr("icon")
if name := node.IALAttr("name"); "" != name {
name = strings.TrimSpace(name)
name = util.EscapeHTML(name)
ret = name
content = name
} else {
ret = getNodeRefText0(node, 1024)
content = getNodeRefText0(node, 1024)
}
//if icon := node.IALAttr("icon"); "" != icon {
// ret = icon + " " + ret
//}
return
}
@ -316,7 +314,7 @@ func getNodeRefText0(node *ast.Node, maxLen int) string {
return ret
}
func GetBlockRefs(defID string) (refIDs, refTexts, defIDs []string) {
func GetBlockRefs(defID string, isBacklink bool) (refIDs, refTexts, defIDs []string) {
refIDs = []string{}
refTexts = []string{}
defIDs = []string{}
@ -332,6 +330,10 @@ func GetBlockRefs(defID string) (refIDs, refTexts, defIDs []string) {
} else {
defIDs = append(defIDs, defID)
}
if isBacklink {
buildBacklinkListItemRefs(&refIDs)
}
return
}
@ -548,3 +550,17 @@ func buildBlockBreadcrumb(node *ast.Node, excludeTypes []string, isEmbedBlock bo
}
return
}
func buildBacklinkListItemRefs(refIDs *[]string) {
refBts := treenode.GetBlockTrees(*refIDs)
for i, refID := range *refIDs {
if bt := refBts[refID]; nil != bt {
if "p" == bt.Type {
if parent := treenode.GetBlockTree(bt.ParentID); nil != parent && "i" == parent.Type {
// 引用计数浮窗请求,需要按照反链逻辑组装 https://github.com/siyuan-note/siyuan/issues/6853
(*refIDs)[i] = parent.ID
}
}
}
}
}

View file

@ -18,6 +18,7 @@ package model
import (
"bytes"
"crypto/sha1"
"fmt"
"os"
"path/filepath"
@ -266,9 +267,10 @@ func InitConf() {
if nil == Conf.Export {
Conf.Export = conf.NewExport()
}
if 0 == Conf.Export.BlockRefMode || 1 == Conf.Export.BlockRefMode {
if 0 == Conf.Export.BlockRefMode || 1 == Conf.Export.BlockRefMode || 5 == Conf.Export.BlockRefMode {
// 废弃导出选项引用块转换为原始块和引述块 https://github.com/siyuan-note/siyuan/issues/3155
Conf.Export.BlockRefMode = 4 // 改为脚注
// 锚点哈希模式和脚注模式合并 https://github.com/siyuan-note/siyuan/issues/13331
Conf.Export.BlockRefMode = 4 // 改为脚注+锚点哈希
}
if "" == Conf.Export.PandocBin {
Conf.Export.PandocBin = util.PandocBinPath
@ -391,6 +393,9 @@ func InitConf() {
if 1 > Conf.Repo.RetentionIndexesDaily {
Conf.Repo.RetentionIndexesDaily = 2
}
if 0 < len(Conf.Repo.Key) {
logging.LogInfof("repo key [%x]", sha1.Sum(Conf.Repo.Key))
}
if nil == Conf.Search {
Conf.Search = conf.NewSearch()

View file

@ -566,11 +566,6 @@ func ExportResources(resourcePaths []string, mainName string) (exportFilePath st
func Preview(id string) (retStdHTML string) {
blockRefMode := Conf.Export.BlockRefMode
if 5 == blockRefMode {
// 如果用户设置的块引导出模式是哈希锚点5则将其强制设置脚注4https://github.com/siyuan-note/siyuan/issues/13283
blockRefMode = 4
}
tree, _ := LoadTreeByBlockID(id)
tree = exportTree(tree, false, false, true,
blockRefMode, Conf.Export.BlockEmbedMode, Conf.Export.FileAnnotationRefMode,
@ -675,13 +670,6 @@ func ExportMarkdownHTML(id, savePath string, docx, merge bool) (name, dom string
}
blockRefMode := Conf.Export.BlockRefMode
if docx {
if 5 == blockRefMode {
// 如果用户设置的块引导出模式是哈希锚点5则将其强制设置脚注4https://github.com/siyuan-note/siyuan/issues/13283
blockRefMode = 4
}
}
tree = exportTree(tree, true, false, true,
blockRefMode, Conf.Export.BlockEmbedMode, Conf.Export.FileAnnotationRefMode,
Conf.Export.TagOpenMarker, Conf.Export.TagCloseMarker,
@ -839,11 +827,6 @@ func ExportHTML(id, savePath string, pdf, image, keepFold, merge bool) (name, do
link.AppendChild(&ast.Node{Type: ast.NodeCloseParen})
h.PrependChild(link)
}
if 5 == blockRefMode {
// 如果用户设置的块引导出模式是哈希锚点5则将其强制设置脚注4https://github.com/siyuan-note/siyuan/issues/13283
blockRefMode = 4
}
}
tree = exportTree(tree, true, keepFold, true,
@ -1419,11 +1402,39 @@ func ExportStdMarkdown(id string) string {
if IsSubscriber() {
cloudAssetsBase = util.GetCloudAssetsServer() + Conf.GetUser().UserId + "/"
}
var defBlockIDs []string
if 4 == Conf.Export.BlockRefMode { // 脚注+锚点哈希
// 导出锚点哈希,这里先记录下所有定义块的 ID
ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
if !entering {
return ast.WalkContinue
}
var defID string
if treenode.IsBlockLink(n) {
defID = strings.TrimPrefix(n.TextMarkAHref, "siyuan://blocks/")
} else if treenode.IsBlockRef(n) {
defID, _, _ = treenode.GetBlockRef(n)
}
if "" != defID {
if defBt := treenode.GetBlockTree(defID); nil != defBt {
defBlockIDs = append(defBlockIDs, defID)
defBlockIDs = gulu.Str.RemoveDuplicatedElem(defBlockIDs)
}
}
return ast.WalkContinue
})
}
defBlockIDs = gulu.Str.RemoveDuplicatedElem(defBlockIDs)
return exportMarkdownContent0(tree, cloudAssetsBase, false,
Conf.Export.BlockRefMode, Conf.Export.BlockEmbedMode, Conf.Export.FileAnnotationRefMode,
Conf.Export.TagOpenMarker, Conf.Export.TagCloseMarker,
Conf.Export.BlockRefTextLeft, Conf.Export.BlockRefTextRight,
Conf.Export.AddTitle, nil)
Conf.Export.AddTitle, defBlockIDs)
}
func BatchExportPandocConvertZip(ids []string, pandocTo, ext string) (name, zipPath string) {
@ -1445,7 +1456,7 @@ func BatchExportPandocConvertZip(ids []string, pandocTo, ext string) (name, zipP
}
docPaths = util.FilterSelfChildDocs(docPaths)
zipPath = exportPandocConvertZip(false, box.ID, baseFolderName, docPaths, "gfm+footnotes+hard_line_breaks", pandocTo, ext)
zipPath = exportPandocConvertZip(baseFolderName, docPaths, "gfm+footnotes+hard_line_breaks", pandocTo, ext)
name = util.GetTreeID(block.Path)
return
}
@ -1469,7 +1480,7 @@ func ExportPandocConvertZip(id, pandocTo, ext string) (name, zipPath string) {
docPaths = append(docPaths, docFile.path)
}
zipPath = exportPandocConvertZip(false, boxID, baseFolderName, docPaths, "gfm+footnotes+hard_line_breaks", pandocTo, ext)
zipPath = exportPandocConvertZip(baseFolderName, docPaths, "gfm+footnotes+hard_line_breaks", pandocTo, ext)
name = util.GetTreeID(block.Path)
return
}
@ -1497,7 +1508,7 @@ func ExportNotebookMarkdown(boxID, folderPath string) (zipPath string) {
for _, docFile := range docFiles {
docPaths = append(docPaths, docFile.path)
}
zipPath = exportPandocConvertZip(true, boxID, baseFolderName, docPaths, "", "", ".md")
zipPath = exportPandocConvertZip(baseFolderName, docPaths, "", "", ".md")
return
}
@ -2004,10 +2015,10 @@ func exportMarkdownContent0(tree *parse.Tree, cloudAssetsBase string, assetsDest
}
}
if 5 == blockRefMode { // 锚点哈希
if 4 == blockRefMode { // 脚注+锚点哈希
if n.IsBlock() && gulu.Str.Contains(n.ID, defBlockIDs) {
// 如果是定义块,则在开头处添加锚点
anchorSpan := &ast.Node{Type: ast.NodeInlineHTML, Tokens: []byte("<span id=\"" + n.ID + "\"></span>")}
anchorSpan := treenode.NewSpanAnchor(n.ID)
if ast.NodeDocument != n.Type {
firstLeaf := treenode.FirstLeafBlock(n)
if nil != firstLeaf {
@ -2082,9 +2093,9 @@ func exportTree(tree *parse.Tree, wysiwyg, keepFold, avHiddenCol bool,
depth = 0
blockLink2Ref(ret, ret.ID, &treeCache, &depth)
// 收集引用转脚注
// 收集引用转脚注+锚点哈希
var refFootnotes []*refAsFootnotes
if 4 == blockRefMode { // 块引转脚注
if 4 == blockRefMode {
depth = 0
collectFootnotesDefs(ret, ret.ID, &refFootnotes, &treeCache, &depth)
}
@ -2163,10 +2174,10 @@ func exportTree(tree *parse.Tree, wysiwyg, keepFold, avHiddenCol bool,
}
n.InsertBefore(blockRefLink)
unlinks = append(unlinks, n)
case 4: // 脚注
case 4: // 脚注+锚点哈希
if currentTreeNodeIDs[defID] {
// 当前文档内不转换脚注,直接使用锚点哈希 https://github.com/siyuan-note/siyuan/issues/13283
n.TextMarkType = "a"
n.TextMarkType = strings.ReplaceAll(n.TextMarkType, "block-ref", "a")
n.TextMarkTextContent = linkText
n.TextMarkAHref = "#" + defID
return ast.WalkContinue
@ -2180,8 +2191,6 @@ func exportTree(tree *parse.Tree, wysiwyg, keepFold, avHiddenCol bool,
n.InsertBefore(&ast.Node{Type: ast.NodeText, Tokens: []byte(linkText)})
n.InsertBefore(&ast.Node{Type: ast.NodeFootnotesRef, Tokens: []byte("^" + refFoot.refNum), FootnotesRefId: refFoot.refNum, FootnotesRefLabel: []byte("^" + refFoot.refNum)})
unlinks = append(unlinks, n)
case 5: // 锚点哈希
// 此处不做任何处理
}
if nil != n.Next && ast.NodeKramdownSpanIAL == n.Next.Type {
@ -2194,7 +2203,7 @@ func exportTree(tree *parse.Tree, wysiwyg, keepFold, avHiddenCol bool,
n.Unlink()
}
if 4 == blockRefMode { // 块引转脚注
if 4 == blockRefMode { // 脚注+锚点哈希
unlinks = nil
footnotesDefBlock := resolveFootnotesDefs(&refFootnotes, ret, currentTreeNodeIDs, blockRefTextLeft, blockRefTextRight, &treeCache)
if nil != footnotesDefBlock {
@ -2243,6 +2252,11 @@ func exportTree(tree *parse.Tree, wysiwyg, keepFold, avHiddenCol bool,
title.AppendChild(&ast.Node{Type: ast.NodeText, Tokens: []byte(content)})
ret.Root.PrependChild(title)
}
} else {
if 4 == blockRefMode { // 脚注+锚点哈希
anchorSpan := treenode.NewSpanAnchor(id)
ret.Root.PrependChild(anchorSpan)
}
}
// 导出时支持导出题头图 https://github.com/siyuan-note/siyuan/issues/4372
@ -3064,7 +3078,7 @@ func processFileAnnotationRef(refID string, n *ast.Node, fileAnnotationRefMode i
return ast.WalkSkipChildren
}
func exportPandocConvertZip(exportNotebook bool, boxID, baseFolderName string, docPaths []string,
func exportPandocConvertZip(baseFolderName string, docPaths []string,
pandocFrom, pandocTo, ext string) (zipPath string) {
dir, name := path.Split(baseFolderName)
name = util.FilterFileName(name)
@ -3074,7 +3088,6 @@ func exportPandocConvertZip(exportNotebook bool, boxID, baseFolderName string, d
name += "_"
}
baseFolderName = path.Join(dir, name)
box := Conf.Box(boxID)
exportFolder := filepath.Join(util.TempDir, "export", baseFolderName+ext)
os.RemoveAll(exportFolder)
@ -3085,7 +3098,7 @@ func exportPandocConvertZip(exportNotebook bool, boxID, baseFolderName string, d
exportRefMode := Conf.Export.BlockRefMode
var defBlockIDs []string
if 5 == exportRefMode {
if 4 == exportRefMode { // 脚注+锚点哈希
// 导出锚点哈希,这里先记录下所有定义块的 ID
walked := map[string]bool{}
for _, p := range docPaths {
@ -3093,29 +3106,32 @@ func exportPandocConvertZip(exportNotebook bool, boxID, baseFolderName string, d
continue
}
docIAL := box.docIAL(p)
if nil == docIAL {
continue
}
id := docIAL["id"]
id := util.GetTreeID(p)
tree, err := LoadTreeByBlockID(id)
if err != nil {
continue
}
ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
if !entering || !treenode.IsBlockRef(n) {
if !entering {
return ast.WalkContinue
}
defID, _, _ := treenode.GetBlockRef(n)
if defBt := treenode.GetBlockTree(defID); nil != defBt {
docPaths = append(docPaths, defBt.Path)
docPaths = gulu.Str.RemoveDuplicatedElem(docPaths)
var defID string
if treenode.IsBlockLink(n) {
defID = strings.TrimPrefix(n.TextMarkAHref, "siyuan://blocks/")
defBlockIDs = append(defBlockIDs, defID)
defBlockIDs = gulu.Str.RemoveDuplicatedElem(defBlockIDs)
} else if treenode.IsBlockRef(n) {
defID, _, _ = treenode.GetBlockRef(n)
}
walked[defBt.Path] = true
if "" != defID {
if defBt := treenode.GetBlockTree(defID); nil != defBt {
docPaths = append(docPaths, defBt.Path)
docPaths = gulu.Str.RemoveDuplicatedElem(docPaths)
defBlockIDs = append(defBlockIDs, defID)
defBlockIDs = gulu.Str.RemoveDuplicatedElem(defBlockIDs)
walked[defBt.Path] = true
}
}
return ast.WalkContinue
})
@ -3126,12 +3142,7 @@ func exportPandocConvertZip(exportNotebook bool, boxID, baseFolderName string, d
luteEngine := util.NewLute()
for _, p := range docPaths {
docIAL := box.docIAL(p)
if nil == docIAL {
continue
}
id := docIAL["id"]
id := util.GetTreeID(p)
hPath, md := exportMarkdownContent(id, exportRefMode, defBlockIDs)
dir, name = path.Split(hPath)
dir = util.FilterFilePath(dir) // 导出文档时未移除不支持的文件名符号 https://github.com/siyuan-note/siyuan/issues/4590

View file

@ -634,14 +634,6 @@ func GetDoc(startID, endID, id string, index int, query string, queryTypes map[s
}
}
if isBacklink { // 引用计数浮窗请求,需要按照反链逻辑组装 https://github.com/siyuan-note/siyuan/issues/6853
if ast.NodeParagraph == node.Type {
if nil != node.Parent && ast.NodeListItem == node.Parent.Type {
node = node.Parent
}
}
}
located := false
isDoc := ast.NodeDocument == node.Type
isHeading := ast.NodeHeading == node.Type
@ -801,6 +793,7 @@ func GetDoc(startID, endID, id string, index int, query string, queryTypes map[s
subTree := &parse.Tree{ID: rootID, Root: &ast.Node{Type: ast.NodeDocument}, Marks: tree.Marks}
query = filterQueryInvisibleChars(query)
if "" != query && (0 == queryMethod || 1 == queryMethod || 3 == queryMethod) { // 只有关键字、查询语法和正则表达式搜索支持高亮
if 0 == queryMethod {
query = stringQuery(query)
@ -883,6 +876,15 @@ func GetDoc(startID, endID, id string, index int, query string, queryTypes map[s
luteEngine.RenderOptions.NodeIndexStart = index
dom = luteEngine.Tree2BlockDOM(subTree, luteEngine.RenderOptions)
if 1 > len(keywords) {
keywords = []string{}
}
for i, keyword := range keywords {
keyword = strings.TrimPrefix(keyword, "#")
keyword = strings.TrimSuffix(keyword, "#")
keywords[i] = keyword
}
go setRecentDocByTree(tree)
return
}

View file

@ -157,7 +157,7 @@ func refreshRefCount(rootID, blockID string) {
for _, count := range refCounts {
rootRefCount += count
}
refIDs, _, _ := GetBlockRefs(blockID)
refIDs, _, _ := GetBlockRefs(blockID, false)
util.PushSetDefRefCount(rootID, blockID, refIDs, refCount, rootRefCount)
}
@ -269,7 +269,11 @@ func refreshDynamicRefTexts(updatedDefNodes map[string]*ast.Node, updatedTrees m
for _, blockValue := range blockValues.Values {
if blockValue.Block.ID == updatedDefNode.ID {
newContent := getNodeAvBlockText(updatedDefNode)
newIcon, newContent := getNodeAvBlockText(updatedDefNode)
if newIcon != blockValue.Block.Icon {
blockValue.Block.Icon = newIcon
changedAv = true
}
if newContent != blockValue.Block.Content {
blockValue.Block.Content = newContent
changedAv = true

View file

@ -19,6 +19,7 @@ package model
import (
"bytes"
"crypto/rand"
"crypto/sha1"
"crypto/sha256"
"encoding/base64"
"errors"
@ -555,6 +556,7 @@ func ImportRepoKey(base64Key string) (retKey string, err error) {
Conf.Repo.Key = key
Conf.Save()
logging.LogInfof("imported repo key [%x]", sha1.Sum(Conf.Repo.Key))
if err = os.RemoveAll(Conf.Repo.GetSaveDir()); err != nil {
return
@ -669,6 +671,7 @@ func InitRepoKeyFromPassphrase(passphrase string) (err error) {
Conf.Repo.Key = key
Conf.Save()
logging.LogInfof("inited repo key [%x]", sha1.Sum(Conf.Repo.Key))
initDataRepo()
return
@ -705,6 +708,7 @@ func InitRepoKey() (err error) {
}
Conf.Repo.Key = key
Conf.Save()
logging.LogInfof("inited repo key [%x]", sha1.Sum(Conf.Repo.Key))
initDataRepo()
return

View file

@ -1459,8 +1459,7 @@ func fullTextSearchCountByFTS(query, boxFilter, pathFilter, typeFilter, ignoreFi
}
func fullTextSearchByLikeWithRoot(query, boxFilter, pathFilter, typeFilter, ignoreFilter, orderBy string, beforeLen, page, pageSize int) (ret []*Block, matchedBlockCount, matchedRootCount int) {
query = strings.ReplaceAll(query, "'", "''")
query = strings.ReplaceAll(query, "\"", "\"\"")
query = strings.ReplaceAll(query, "'", "''") // 不需要转义双引号,因为条件都是通过单引号包裹的,只需要转义单引号即可
keywords := strings.Split(query, " ")
contentField := columnConcat()
var likeFilter string

View file

@ -24,6 +24,7 @@ import (
"github.com/88250/gulu"
"github.com/88250/lute/lex"
"github.com/siyuan-note/siyuan/kernel/util"
)
func MarkText(text string, keyword string, beforeLen int, caseSensitive bool) (pos int, marked string) {
@ -112,7 +113,7 @@ func EncloseHighlighting(text string, keywords []string, openMark, closeMark str
ret = text
if reg, err := regexp.Compile(re); err == nil {
ret = reg.ReplaceAllStringFunc(text, func(s string) string { return openMark + s + closeMark })
ret = reg.ReplaceAllStringFunc(text, func(s string) string { return openMark + util.EscapeHTML(s) + closeMark })
}
// 搜索结果预览包含转义符问题 Search results preview contains escape character issue https://github.com/siyuan-note/siyuan/issues/9790

View file

@ -116,3 +116,7 @@ func NewParagraph(id string) (ret *ast.Node) {
ret.SetIALAttr("updated", newID[:14])
return
}
func NewSpanAnchor(id string) (ret *ast.Node) {
return &ast.Node{Type: ast.NodeInlineHTML, Tokens: []byte("<span id=\"" + id + "\" style=\"display: none;\"></span>")}
}