Merge branch 'siyuan-note:dev' into dev
This commit is contained in:
commit
531d7389a3
64 changed files with 397 additions and 310 deletions
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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": "בחר את המסמך הפתוח",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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": "開いているドキュメントをツリーで選択",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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": "Выбрать открытый документ",
|
||||
|
|
|
@ -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": "定位打開的文檔",
|
||||
|
|
|
@ -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": "定位打开的文档",
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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>`;
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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="["${data.blockID}"]" 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="["${data.blockID}"]" data-id="${JSON.stringify(data.refIDs)}" style="">${data.rootRefCount}</div>`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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 => {
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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"`;
|
||||
|
|
|
@ -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}">
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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}">
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -743,4 +743,4 @@ export const clearTableCell = (protyle: IProtyle, tableBlockElement: HTMLElement
|
|||
item.innerHTML = "";
|
||||
});
|
||||
updateTransaction(protyle, tableBlockElement.getAttribute("data-node-id"), tableBlockElement.outerHTML, oldHTML);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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"}">
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
13
app/src/types/config.d.ts
vendored
13
app/src/types/config.d.ts
vendored
|
@ -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
|
||||
|
|
2
app/src/types/index.d.ts
vendored
2
app/src/types/index.d.ts
vendored
|
@ -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
|
||||
|
|
2
app/src/types/protyle.d.ts
vendored
2
app/src/types/protyle.d.ts
vendored
|
@ -484,7 +484,7 @@ interface IProtyle {
|
|||
mark: Highlight
|
||||
markHL: Highlight
|
||||
ranges: Range[]
|
||||
rangeIndex: 0
|
||||
rangeIndex: number
|
||||
styleElement: HTMLStyleElement
|
||||
}
|
||||
getInstance: () => import("../protyle").Protyle,
|
||||
|
|
|
@ -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); // 移动端需要加载完才可以获取到颜色
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -1152,6 +1152,7 @@ func getDoc(c *gin.Context) {
|
|||
"isSyncing": isSyncing,
|
||||
"isBacklinkExpand": isBacklinkExpand,
|
||||
"keywords": keywords,
|
||||
"reqId": arg["reqId"],
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"`
|
||||
|
|
|
@ -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"` // 内容块引用导出锚文本左侧符号,默认留空
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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=
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)则将其强制设置脚注(4)https://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)则将其强制设置脚注(4)https://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)则将其强制设置脚注(4)https://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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>")}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue