Merge branch 'siyuan-note:dev' into dev

This commit is contained in:
W.Kai 2024-12-14 23:02:26 +08:00 committed by GitHub
commit 5abebc0316
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
112 changed files with 3288 additions and 1007 deletions

View file

@ -1,4 +1,6 @@
{
"second": "Sekunde",
"syncInterval": "Synchronisierungsintervall",
"removeAV": "Aus der Datenbank entfernen",
"empty": "Leer",
"newRowInRelation": "Erstellen Sie einen neuen Eintrag in ${x} <b class='ft__on-surface'>${y}</b>",
@ -768,7 +770,7 @@
"openSyncTip2": "Beim Wechsel von deaktiviert auf aktiviert wird empfohlen, manuell auf die Synchronisationstaste zu klicken, um eine Synchronisation auszulösen.",
"syncMode": "Cloud-Synchronisationsmodus",
"syncModeTip": "Der Synchronisationsmodus kann nach der Aktivierung der Cloud-Synchronisation weiter ausgewählt werden.",
"syncMode1": "Automatisch (Synchronisation alle 30 Sekunden, nachdem sich die Daten nicht mehr ändern)",
"syncMode1": "Automatisch",
"syncMode2": "Manuell (automatische Synchronisation nur einmal beim Starten und Schließen der Software, andere Zeitpunkte müssen manuell ausgelöst werden)",
"syncMode3": "Vollständige manuelle Synchronisation (keine Synchronisation beim Start und beim Schließen, vollständige manuelle Kontrolle über den Synchronisationszeitpunkt und die Synchronisationsrichtung)",
"cloudSync": "Cloud-Synchronisation",

View file

@ -768,7 +768,7 @@
"openSyncTip2": "When changing from disabled to enabled, it is recommended to manually click the sync button to trigger a synchronization",
"syncMode": "Cloud sync mode",
"syncModeTip": "The sync mode can be further selected after cloud sync is enabled",
"syncMode1": "Auto (sync every 30 seconds after the data no longer changes)",
"syncMode1": "Auto",
"syncMode2": "Manual (automatic auto only once when the software is started and closed, other times need to manually trigger)",
"syncMode3": "Full manual sync (no sync on startup and shutdown, full manual control of sync timing and sync direction)",
"cloudSync": "Cloud sync",

View file

@ -1,4 +1,8 @@
{
"second": "second",
"syncInterval": "Sync interval",
"second": "segundo",
"syncInterval": "intervalo de sincronización",
"removeAV": "Eliminar de la base de datos",
"empty": "Vacío",
"newRowInRelation": "Crear una nueva entrada en ${x} <b class='ft__on-surface'>${y}</b>",
@ -768,7 +772,7 @@
"openSyncTip2": "Al cambiar de desactivado a activado, se recomienda hacer clic manualmente en el botón de sincronización para activar una sincronización",
"syncMode": "Modo de sincronización en la nube",
"syncModeTip": "El modo de sincronización se puede seguir seleccionando después de habilitar la sincronización en la nube",
"syncMode1": "Auto (sincronización cada 30 segundos después de que los datos dejen de cambiar)",
"syncMode1": "Auto",
"syncMode2": "Manual (automático sólo una vez cuando se inicia y se cierra el software, otras veces hay que activarlo manualmente)",
"syncMode3": "Manual Completa (sin sincronización al iniciar y apagar, control manual completo de sincronización y dirección de sincronización)",
"cloudSync": "Sincronización en la nube",
@ -1320,7 +1324,7 @@
"quit": "Salir de la aplicación"
},
"_attrView": {
"tabla": "Tabla",
"table": "Tabla",
"key": "Clave principal",
"select": "Selección"
},

View file

@ -1,4 +1,6 @@
{
"second": "seconde",
"syncInterval": "intervalle de synchronisation",
"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>",
@ -768,7 +770,7 @@
"openSyncTip2": "Lorsque vous passez de la désactivation à l'activation, il est recommandé de cliquer manuellement sur le bouton de synchronisation pour déclencher une synchronisation",
"syncMode": "Mode de synchronisation cloud",
"syncModeTip": "Le mode de synchronisation peut être sélectionné davantage après l'activation de la synchronisation dans le cloud",
"syncMode1": "Automatique (synchronisation toutes les 30 secondes après que les données ne changent plus)",
"syncMode1": "Automatique",
"syncMode2": "Manuelle (synchronisation automatique une seule fois lorsque le logiciel est démarré et fermé, les autres fois, il faut déclencher manuellement la synchronisation)",
"syncMode3": "Manuelle Complète (pas de synchronisation au démarrage et à l'arrêt, contrôle manuel complet de la synchronisation et du sens de synchronisation)",
"cloudSync": "Cloud Sync",

View file

@ -1,4 +1,6 @@
{
"second": "שנייה",
"syncInterval": "מרווח סנכרון",
"removeAV": "הסר ממסד הנתונים",
"empty": "ריק",
"newRowInRelation": "צור ערך חדש ב-${x} <b class='ft__on-surface'>${y}</b>",
@ -768,7 +770,7 @@
"openSyncTip2": "כאשר אתה משנה מ-מושבת ל-פעיל, מומלץ ללחוץ ידנית על כפתור הסנכרון כדי להפעיל סנכרון",
"syncMode": "מצב סנכרון בענן",
"syncModeTip": "מצב הסנכרון ניתן לבחירה נוספת לאחר שהסנכרון בענן הופעל",
"syncMode1": "אוטומטי (סנכרון כל 30 שניות לאחר שהנתונים לא משתנים יותר)",
"syncMode1": "אוטומטי",
"syncMode2": "ידני (אוטומטית בהחלט פעם אחת בעת הפעלת התוכנה והכיבוי, פעמים אחרות יש להפעיל ידנית)",
"syncMode3": "סנכרון ידני מלא (אין סנכרון בעת הפעלה וכיבוי, את שליטת הזמן והכיוונים של הסנכרון באופן ידני)",
"cloudSync": "סנכרון בענן",

View file

@ -1,4 +1,6 @@
{
"second": "secondo",
"syncInterval": "intervallo di sincronizzazione",
"removeAV": "Rimuovi dal database",
"empty": "Vuoto",
"newRowInRelation": "Crea una nuova voce in ${x} <b class='ft__on-surface'>${y}</b>",
@ -768,7 +770,7 @@
"openSyncTip2": "Quando si cambia da disabilitato ad abilitato, si consiglia di fare clic manualmente sul pulsante di sincronizzazione per avviare la sincronizzazione",
"syncMode": "Modalità di sincronizzazione cloud",
"syncModeTip": "La modalità di sincronizzazione può essere ulteriormente selezionata dopo l'abilitazione della sincronizzazione cloud",
"syncMode1": "Auto (sincronizza ogni 30 secondi dopo che i dati non cambiano più)",
"syncMode1": "Auto",
"syncMode2": "Manuale (auto automatico solo una volta quando il software viene avviato e chiuso, altre volte richiede l'attivazione manuale)",
"syncMode3": "Sincronizzazione completamente manuale (nessuna sincronizzazione all'avvio e alla chiusura, controllo manuale completo del momento e della direzione della sincronizzazione)",
"cloudSync": "Sincronizzazione cloud",

View file

@ -1,4 +1,6 @@
{
"second": "秒",
"syncInterval": "同期間隔",
"removeAV": "データベースから削除",
"empty": "空白",
"newRowInRelation": "${x} に新しい項目を作成 <b class='ft__on-surface'>${y}</b>",
@ -768,7 +770,7 @@
"openSyncTip2": "無効から有効に変更した場合は手動で同期ボタンをクリックして同期をトリガーすることをお勧めします",
"syncMode": "クラウド同期モード",
"syncModeTip": "クラウド同期を有効にすると同期モードを選択できます",
"syncMode1": "自動 (起動時と終了時に同期され、アプリケーションの使用中は同期アルゴリズムにしたがって自動的に同期されます)",
"syncMode1": "自動",
"syncMode2": "手動 (起動時と終了時に一度だけ同期され、それ以外の場合は手動で同期をトリガーする必要があります)",
"syncMode3": "完全手動同期 (起動と終了時に同期されず、同期タイミングと同期方向をすべて手動で制御します)",
"cloudSync": "クラウド同期",

View file

@ -1,4 +1,6 @@
{
"second": "sekunda",
"syncInterval": "interwał synchronizacji",
"removeAV": "Usuń z bazy danych",
"empty": "Pusty",
"newRowInRelation": "Utwórz nowy wpis w ${x} <b class='ft__on-surface'>${y}</b>",
@ -768,7 +770,7 @@
"openSyncTip2": "Podczas zmiany z wyłączonej na włączoną, zaleca się ręczne kliknięcie przycisku synchronizacji, aby wyzwolić synchronizację",
"syncMode": "Tryb synchronizacji w chmurze",
"syncModeTip": "Tryb synchronizacji można dalej wybrać po włączeniu synchronizacji w chmurze",
"syncMode1": "Auto (synchronizacja co 30 sekund po tym, jak dane przestają się zmieniać)",
"syncMode1": "Auto",
"syncMode2": "Ręczny (automatyczna synchronizacja tylko raz przy uruchamianiu i zamykaniu oprogramowania, inne czasy wymagają ręcznego wyzwolenia)",
"syncMode3": "W pełni ręczna synchronizacja (brak synchronizacji przy uruchamianiu i zamykaniu, pełna ręczna kontrola nad czasem synchronizacji i kierunkiem synchronizacji)",
"cloudSync": "Synchronizacja w chmurze",

View file

@ -1,4 +1,6 @@
{
"second": "секунда",
"syncInterval": "интервал синхронизации",
"removeAV": "Удалить из базы данных",
"empty": "Пусто",
"newRowInRelation": "Создать новую запись в ${x} <b class='ft__on-surface'>${y}</b>",
@ -768,7 +770,7 @@
"openSyncTip2": "При изменении с отключенного на включенное рекомендуется вручную нажать кнопку синхронизации для запуска синхронизации",
"syncMode": "Режим облачной синхронизации",
"syncModeTip": "Режим синхронизации можно дополнительно выбрать после включения облачной синхронизации",
"syncMode1": "Авто (синхронизация каждые 30 секунд, когда данные больше не изменяются)",
"syncMode1": "Авто",
"syncMode2": "Ручной (автоматическая синхронизация только один раз при запуске и закрытии программы, в другие разы необходимо вручную запустить)",
"syncMode3": "Полная ручная синхронизация (не синхронизируется при запуске и завершении, полный ручной контроль времени и направления синхронизации)",
"cloudSync": "Облачная синхронизация",

View file

@ -1,4 +1,6 @@
{
"second": "秒",
"syncInterval": "同步間隔",
"removeAV": "從資料庫中移除",
"empty": "空白",
"newRowInRelation": "在 ${x} 中新建條目 <b class='ft__on-surface'>${y}</b>",
@ -768,7 +770,7 @@
"openSyncTip2": "從禁用改為啟用時建議手動點擊同步按鈕觸發一次同步",
"syncMode": "雲端同步模式",
"syncModeTip": "啟用雲端同步後可進一步選擇同步模式",
"syncMode1": "自動同步(資料不再變動後 30 秒進行一次同步)",
"syncMode1": "自動同步",
"syncMode2": "手動同步(僅啟動和關閉軟體時自動同步一次,其他時候需要手動觸發同步)",
"syncMode3": "完全手動同步(啟動和關閉時均不同步,完全手動控制同步時機和同步方向)",
"cloudSync": "雲端同步",

View file

@ -1,4 +1,7 @@
{
"second": "秒",
"syncInterval": "同步间隔",
"syncIntervalTip": "数据不再变动后自动进行数据同步",
"removeAV": "从数据库中移除",
"empty": "空白",
"newRowInRelation": "在 ${x} 中新建条目 <b class='ft__on-surface'>${y}</b>",
@ -768,7 +771,7 @@
"openSyncTip2": "从禁用改为启用时建议手动点击同步按钮触发一次同步",
"syncMode": "云端同步模式",
"syncModeTip": "启用云端同步后可进一步选择同步模式",
"syncMode1": "自动同步(数据不再变动后 30 秒进行一次同步)",
"syncMode1": "自动同步",
"syncMode2": "手动同步(仅启动和关闭软件时自动同步一次,其他时候需要手动触发同步)",
"syncMode3": "完全手动同步(启动和关闭时均不同步,完全手动控制同步时机和同步方向)",
"cloudSync": "云端同步",

View file

@ -4,7 +4,7 @@
--b3-theme-primary-light: rgba(53, 117, 240, .54);
--b3-theme-primary-lighter: rgba(53, 117, 240, .38);
--b3-theme-primary-lightest: rgba(53, 117, 240, .12);
--b3-theme-secondary: #f3a92f;
--b3-theme-secondary: #ff9200;
--b3-theme-background: #fff;
--b3-theme-background-light: #dfe0e1;
--b3-theme-surface: #f6f6f6;
@ -23,11 +23,11 @@
/* 字体 */
/* Windows 斜体遮挡添加 "Segoe UI" 字体 https://github.com/siyuan-note/siyuan/issues/11841 */
--b3-font-family: "Helvetica Neue", "Luxi Sans", "DejaVu Sans", "Hiragino Sans GB", "Segoe UI", "Microsoft Yahei", sans-serif, "Apple Color Emoji", "Noto Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Android Emoji", "EmojiSymbols";
--b3-font-family-protyle: var(--b3-font-family);
--b3-font-family-code: "JetBrainsMono-Regular", mononoki, Consolas, "Liberation Mono", Menlo, Courier, monospace, var(--b3-font-family);
--b3-font-family: "Helvetica Neue", "Luxi Sans", "DejaVu Sans", "Hiragino Sans", "Segoe UI", sans-serif, "Arial", "Tahoma", "Emojis";
--b3-font-family-protyle: "Number Glyphs", "SiYuan Emojis", var(--b3-font-family);
--b3-font-family-code: "Number Glyphs Of JetBrainsMono-Regular", "SiYuan Emojis", "JetBrainsMono-Regular", mononoki, Consolas, "Liberation Mono", Menlo, Courier, monospace, var(--b3-font-family);
--b3-font-family-graph: arial;
--b3-font-family-emoji: "Segoe UI Emoji", "Segoe UI Symbol", "Segoe UI", "Apple Color Emoji", "Twemoji Mozilla", "Noto Color Emoji", "Android Emoji";
--b3-font-family-emoji: "SiYuan Emojis", "Emojis";
--b3-font-family-math: KaTeX_Math;
--b3-font-size: 14px;
@ -41,6 +41,7 @@
/* 线条 */
--b3-border-color: var(--b3-theme-surface-lighter);
--b3-border-radius: 6px;
--b3-border-radius-s: 3px;
--b3-border-radius-b: 12px;
/* 滚动条 */
@ -66,13 +67,13 @@
--b3-mask-background: rgba(220, 220, 220, .4);
/* 卡片背景 */
--b3-card-error-color: rgb(97, 26, 21);
--b3-card-error-color: #790600;
--b3-card-error-background: #f5d1cf;
--b3-card-warning-color: rgb(102, 60, 0);
--b3-card-warning-color: #b16700;
--b3-card-warning-background: #ffe8c8;
--b3-card-info-color: rgb(13, 60, 97);
--b3-card-info-color: #005599;
--b3-card-info-background: #d6eaf9;
--b3-card-success-color: rgb(30, 70, 32);
--b3-card-success-color: #008606;
--b3-card-success-background: #d7eed8;
/* 自定义文字 */
@ -85,9 +86,9 @@
--b3-font-color7: var(--b3-theme-secondary);
--b3-font-color8: var(--b3-theme-error);
--b3-font-color9: #f5539e;
--b3-font-color10: #944194;
--b3-font-color11: #65b84d;
--b3-font-color12: #f5822e;
--b3-font-color10: #00cdcd;
--b3-font-color11: #00b853;
--b3-font-color12: #9e9700;
--b3-font-color13: var(--b3-theme-background);
--b3-font-background1: var(--b3-card-error-background);
--b3-font-background2: var(--b3-card-warning-background);
@ -95,12 +96,12 @@
--b3-font-background4: var(--b3-card-success-background);
--b3-font-background5: #e2e3e4;
--b3-font-background6: #acd0fc;
--b3-font-background7: #fdeed6;
--b3-font-background8: #fae1cf;
--b3-font-background9: #fdd5e7;
--b3-font-background10: #e6c7e6;
--b3-font-background11: #def0d9;
--b3-font-background12: #fae3e4;
--b3-font-background7: #fddaab;
--b3-font-background8: #ffb0a9;
--b3-font-background9: #fdbfff;
--b3-font-background10: #b1ffff;
--b3-font-background11: #affad1;
--b3-font-background12: #fff88f;
--b3-font-background13: var(--b3-theme-on-background);
/* 动画效果 */
@ -200,8 +201,16 @@
--b3-parent-background: var(--b3-theme-background);
}
:lang(ja_JP):root {
--b3-font-family: "Helvetica Neue", "Luxi Sans", "DejaVu Sans", "Hiragino Sans", "Yu Gothic UI", "Segoe UI", sans-serif, "Apple Color Emoji", "Noto Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Android Emoji", "EmojiSymbols";
:root:lang(zh_CN) {
--b3-font-family: "Helvetica Neue", "PingFang SC", "Luxi Sans", "DejaVu Sans", "Hiragino Sans GB", "Segoe UI", "Source Han Sans SC", "Microsoft Yahei", sans-serif, "Arial", "Tahoma", "Emojis";
}
:root:lang(zh_CHT) {
--b3-font-family: "Helvetica Neue", "PingFang TC", "Luxi Sans", "DejaVu Sans", "Hiragino Sans TC", "Segoe UI", "Source Han Sans TC", "Microsoft JhengHei", sans-serif, "Arial", "Tahoma", "Emojis";
}
:root:lang(ja_JP) {
--b3-font-family: "Helvetica Neue", "Luxi Sans", "DejaVu Sans", "Hiragino Sans", "Yu Gothic UI", "Segoe UI", sans-serif, "Arial", "Tahoma", "Emojis";
}
/* https://github.com/siyuan-note/siyuan/issues/6440 */

View file

@ -2,7 +2,7 @@
"name": "daylight",
"author": "Vanessa",
"url": "https://github.com/Vanessa219",
"version": "1.0.7",
"version": "1.0.8",
"modes": [
"light"
]

View file

@ -22,11 +22,11 @@
--b3-theme-on-error: #fff;
/* 字体 */
--b3-font-family: "Helvetica Neue", "Luxi Sans", "DejaVu Sans", "Hiragino Sans GB", "Segoe UI", "Microsoft Yahei", sans-serif, "Apple Color Emoji", "Noto Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Android Emoji", "EmojiSymbols";
--b3-font-family-protyle: var(--b3-font-family);
--b3-font-family-code: "JetBrainsMono-Regular", mononoki, Consolas, "Liberation Mono", Menlo, Courier, monospace, var(--b3-font-family);
--b3-font-family: "Helvetica Neue", "Luxi Sans", "DejaVu Sans", "Hiragino Sans", "Segoe UI", sans-serif, "Arial", "Tahoma", "Emojis";
--b3-font-family-protyle: "Number Glyphs", "SiYuan Emojis", var(--b3-font-family);
--b3-font-family-code: "Number Glyphs Of JetBrainsMono-Regular", "SiYuan Emojis", "JetBrainsMono-Regular", mononoki, Consolas, "Liberation Mono", Menlo, Courier, monospace, var(--b3-font-family);
--b3-font-family-graph: arial;
--b3-font-family-emoji: "Segoe UI Emoji", "Segoe UI Symbol", "Segoe UI", "Apple Color Emoji", "Twemoji Mozilla", "Noto Color Emoji", "Android Emoji";
--b3-font-family-emoji: "SiYuan Emojis", "Emojis";
--b3-font-family-math: KaTeX_Math;
--b3-font-size: 14px;
@ -40,6 +40,7 @@
/* 线条 */
--b3-border-color: #363636;
--b3-border-radius: 6px;
--b3-border-radius-s: 3px;
--b3-border-radius-b: 12px;
/* 滚动条 */
@ -84,22 +85,22 @@
--b3-font-color7: var(--b3-theme-secondary);
--b3-font-color8: var(--b3-theme-error);
--b3-font-color9: #f5539e;
--b3-font-color10: #bc67bc;
--b3-font-color11: #65b84d;
--b3-font-color12: #f5822e;
--b3-font-color10: #00eeff;
--b3-font-color11: #74ff00;
--b3-font-color12: #fff200;
--b3-font-color13: var(--b3-theme-background);
--b3-font-background1: var(--b3-card-error-background);
--b3-font-background2: var(--b3-card-warning-background);
--b3-font-background3: var(--b3-card-info-background);
--b3-font-background4: var(--b3-card-success-background);
--b3-font-background5: #4c5257;
--b3-font-background6: #0c3d88;
--b3-font-background6: #08296c;
--b3-font-background7: #593905;
--b3-font-background8: #541812;
--b3-font-background9: #6a0634;
--b3-font-background10: #6b2f6b;
--b3-font-background11: #376629;
--b3-font-background12: #803a06;
--b3-font-background9: #843473;
--b3-font-background10: #329096;
--b3-font-background11: #568b2a;
--b3-font-background12: #8d8829;
--b3-font-background13: var(--b3-theme-on-background);
/* 动画效果 */
@ -199,8 +200,16 @@
--b3-parent-background: var(--b3-theme-background);
}
:lang(ja_JP):root {
--b3-font-family: "Helvetica Neue", "Luxi Sans", "DejaVu Sans", "Hiragino Sans", "Yu Gothic UI", "Segoe UI", sans-serif, "Apple Color Emoji", "Noto Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Android Emoji", "EmojiSymbols";
:root:lang(zh_CN) {
--b3-font-family: "Helvetica Neue", "PingFang SC", "Luxi Sans", "DejaVu Sans", "Hiragino Sans GB", "Segoe UI", "Source Han Sans SC", "Microsoft Yahei", sans-serif, "Arial", "Tahoma", "Emojis";
}
:root:lang(zh_CHT) {
--b3-font-family: "Helvetica Neue", "PingFang TC", "Luxi Sans", "DejaVu Sans", "Hiragino Sans TC", "Segoe UI", "Source Han Sans TC", "Microsoft JhengHei", sans-serif, "Arial", "Tahoma", "Emojis";
}
:root:lang(ja_JP) {
--b3-font-family: "Helvetica Neue", "Luxi Sans", "DejaVu Sans", "Hiragino Sans", "Yu Gothic UI", "Segoe UI", sans-serif, "Arial", "Tahoma", "Emojis";
}
/* https://github.com/siyuan-note/siyuan/issues/6440 */

View file

@ -2,7 +2,7 @@
"name": "midnight",
"author": "Vanessa",
"url": "https://github.com/Vanessa219",
"version": "1.0.7",
"version": "1.0.8",
"modes": [
"dark"
]

View file

@ -9,7 +9,7 @@
<Identity Name="89C2A984.SiYuan"
ProcessorArchitecture="x64"
Publisher="CN=087C656E-C1D9-42D8-8807-CED45A74FC0F"
Version="3.1.14.0"/>
Version="3.1.15.0"/>
<Properties>
<DisplayName>SiYuan</DisplayName>
<PublisherDisplayName>云南链滴科技有限公司</PublisherDisplayName>

View file

@ -0,0 +1,53 @@
## Overview
This version supports use on the HarmonyOS NEXT system.
## Changelogs
Below are the detailed changes in this version.
### Enhancement
* [Improve Alt+M hiding/showing windows](https://github.com/siyuan-note/siyuan/issues/12656)
* [Improve database cell positioning and editing input box size](https://github.com/siyuan-note/siyuan/issues/12708)
* [Support HarmonyOS NEXT system](https://github.com/siyuan-note/siyuan/issues/13184)
* [Remove the collapsed state of the collapsed heading on paste](https://github.com/siyuan-note/siyuan/issues/13232)
* [Support the year in the database date field is less than 4 digits](https://github.com/siyuan-note/siyuan/issues/13252)
* [Improve interaction with database descriptions](https://github.com/siyuan-note/siyuan/issues/13262)
* [Improve document tag adding interaction](https://github.com/siyuan-note/siyuan/issues/13311)
* [Improve tooltip](https://github.com/siyuan-note/siyuan/pull/13326)
* [Improve exporting block ref](https://github.com/siyuan-note/siyuan/issues/13331)
* [Improve search highlighting](https://github.com/siyuan-note/siyuan/issues/13343)
* [Improve template search](https://github.com/siyuan-note/siyuan/issues/13348)
* [Improve code block paging when exporting to PDF](https://github.com/siyuan-note/siyuan/issues/13349)
* [Improve HTML clipping](https://github.com/siyuan-note/siyuan/issues/13355)
* [Improve system font loading](https://github.com/siyuan-note/siyuan/issues/13356)
* [Generating block reference with a block that has ' character is causing weird behaviour](https://github.com/siyuan-note/siyuan/issues/13358)
* [Simplify document block paths in search results](https://github.com/siyuan-note/siyuan/issues/13364)
* [The browser clipping extension adds some experimental features](https://github.com/siyuan-note/siyuan/issues/13366)
* [Attribute panel - Database supports removing the current block](https://github.com/siyuan-note/siyuan/issues/13375)
* [Support flac audio asset playback](https://github.com/siyuan-note/siyuan/issues/13386)
* [Cannot double-click to preview them when image url include `%`](https://github.com/siyuan-note/siyuan/issues/13388)
* [Using the middle button to close a tab triggers pasting on Linux](https://github.com/siyuan-note/siyuan/pull/13395)
* [Improve setting appearance priority](https://github.com/siyuan-note/siyuan/issues/13404)
### Bugfix
* [The document in the mobile data history will be closed when you slide down](https://github.com/siyuan-note/siyuan/issues/13347)
* [No results found after clicking on the inline tag](https://github.com/siyuan-note/siyuan/issues/13351)
* [HTML tag search escaping issue](https://github.com/siyuan-note/siyuan/issues/13354)
* [The sequence number of the ordered list exported to .docx is incorrect](https://github.com/siyuan-note/siyuan/issues/13365)
* [File names ending with `.` will be considered as missing assets](https://github.com/siyuan-note/siyuan/issues/13368)
* [Full manual sync mode causes data conflicts](https://github.com/siyuan-note/siyuan/issues/13387)
### Development
* [Improve the operation of `Backspace` `Delete` `Tab` `Shift+Tab` after selecting a block or table cell](https://github.com/siyuan-note/siyuan/issues/13027)
* [Add a kernel API `/api/filetree/moveDocsByID`](https://github.com/siyuan-note/siyuan/issues/13247)
* [Remove editor input console log](https://github.com/siyuan-note/siyuan/issues/13346)
* [Add `disable`, `enable` method to Protyle](https://github.com/siyuan-note/siyuan/issues/13391)
## Download
* [B3log](https://b3log.org/siyuan/en/download.html)
* [GitHub](https://github.com/siyuan-note/siyuan/releases)

View file

@ -0,0 +1,53 @@
## 概述
這個版本支持了鴻蒙系統。
## 變更記錄
以下是此版本中的詳細變更。
### 改進功能
* [改進 Alt+M 隱藏/顯示視窗](https://github.com/siyuan-note/siyuan/issues/12656)
* [改進資料庫單元格定位和編輯輸入框大小](https://github.com/siyuan-note/siyuan/issues/12708)
* [支援鴻蒙系統](https://github.com/siyuan-note/siyuan/issues/13184)
* [貼上時移除折疊標題的折疊狀態](https://github.com/siyuan-note/siyuan/issues/13232)
* [支援資料庫日期欄位少於 4 位元的年份](https://github.com/siyuan-note/siyuan/issues/13252)
* [改進資料庫描述的互動](https://github.com/siyuan-note/siyuan/issues/13262)
* [改進文件標籤添加互動](https://github.com/siyuan-note/siyuan/issues/13311)
* [改進工具提示](https://github.com/siyuan-note/siyuan/pull/13326)
* [改進導出區塊引用](https://github.com/siyuan-note/siyuan/issues/13331)
* [改進搜尋高亮](https://github.com/siyuan-note/siyuan/issues/13343)
* [改進範本搜尋](https://github.com/siyuan-note/siyuan/issues/13348)
* [改進匯出為 PDF 時的程式碼區塊分頁](https://github.com/siyuan-note/siyuan/issues/13349)
* [改進 HTML 剪藏](https://github.com/siyuan-note/siyuan/issues/13355)
* [改進系統字體載入](https://github.com/siyuan-note/siyuan/issues/13356)
* [產生包含 ' 字元的區塊引用時導致奇怪的行為](https://github.com/siyuan-note/siyuan/issues/13358)
* [簡化搜尋結果中的文件區塊路徑](https://github.com/siyuan-note/siyuan/issues/13364)
* [瀏覽器剪藏擴充功能加入了一些實驗性功能](https://github.com/siyuan-note/siyuan/issues/13366)
* [屬性面板 - 資料庫支援移除目前區塊](https://github.com/siyuan-note/siyuan/issues/13375)
* [支援 flac 音訊資源播放](https://github.com/siyuan-note/siyuan/issues/13386)
* [當圖像 URL 包含 `%` 時無法雙擊預覽](https://github.com/siyuan-note/siyuan/issues/13388)
* [在 Linux 上使用滑鼠中鍵關閉標籤頁會觸發貼上](https://github.com/siyuan-note/siyuan/pull/13395)
* [改進設定外觀優先權](https://github.com/siyuan-note/siyuan/issues/13404)
### 修復缺陷
* [行動端資料歷史中的文件在下滑時會關閉](https://github.com/siyuan-note/siyuan/issues/13347)
* [點選行級標籤後找不到結果](https://github.com/siyuan-note/siyuan/issues/13351)
* [HTML 標籤搜​​尋轉義問題](https://github.com/siyuan-note/siyuan/issues/13354)
* [匯出至 .docx 的有序列表序號不正確](https://github.com/siyuan-note/siyuan/issues/13365)
* [以 `.` 結尾的檔案名稱會被視為缺失資源](https://github.com/siyuan-note/siyuan/issues/13368)
* [完全手動同步模式導致資料衝突](https://github.com/siyuan-note/siyuan/issues/13387)
### 開發者
* [改進選擇區塊或表格儲存格後 `Backspace` `Delete` `Tab` `Shift+Tab` 的操作](https://github.com/siyuan-note/siyuan/issues/13027)
* [新增內核 API `/api/filetree/moveDocsByID`](https://github.com/siyuan-note/siyuan/issues/13247)
* [移除編輯器輸入控制台日誌](https://github.com/siyuan-note/siyuan/issues/13346)
* [為 Protyle 新增 `disable` 和 `enable` 方法](https://github.com/siyuan-note/siyuan/issues/13391)
## 下載
* [B3log](https://b3log.org/siyuan/download.html)
* [GitHub](https://github.com/siyuan-note/siyuan/releases)

View file

@ -0,0 +1,53 @@
## 概述
该版本支持了鸿蒙系统。
## 变更记录
以下是此版本中的详细变更。
### 改进功能
* [改进 Alt+M 隐藏/显示窗口](https://github.com/siyuan-note/siyuan/issues/12656)
* [改进数据库单元格定位和编辑输入框大小](https://github.com/siyuan-note/siyuan/issues/12708)
* [支持鸿蒙系统](https://github.com/siyuan-note/siyuan/issues/13184)
* [粘贴时移除折叠标题的折叠状态](https://github.com/siyuan-note/siyuan/issues/13232)
* [支持数据库日期字段少于 4 位的年份](https://github.com/siyuan-note/siyuan/issues/13252)
* [改进数据库描述的交互](https://github.com/siyuan-note/siyuan/issues/13262)
* [改进文档标签添加交互](https://github.com/siyuan-note/siyuan/issues/13311)
* [改进工具提示](https://github.com/siyuan-note/siyuan/pull/13326)
* [改进导出块引用](https://github.com/siyuan-note/siyuan/issues/13331)
* [改进搜索高亮](https://github.com/siyuan-note/siyuan/issues/13343)
* [改进模板搜索](https://github.com/siyuan-note/siyuan/issues/13348)
* [改进导出为 PDF 时的代码块分页](https://github.com/siyuan-note/siyuan/issues/13349)
* [改进 HTML 剪藏](https://github.com/siyuan-note/siyuan/issues/13355)
* [改进系统字体加载](https://github.com/siyuan-note/siyuan/issues/13356)
* [生成包含 ' 字符的块引用时导致奇怪的行为](https://github.com/siyuan-note/siyuan/issues/13358)
* [简化搜索结果中的文档块路径](https://github.com/siyuan-note/siyuan/issues/13364)
* [浏览器剪藏扩展添加了一些实验性功能](https://github.com/siyuan-note/siyuan/issues/13366)
* [属性面板 - 数据库支持移除当前块](https://github.com/siyuan-note/siyuan/issues/13375)
* [支持 flac 音频资源播放](https://github.com/siyuan-note/siyuan/issues/13386)
* [当图像 URL 包含 `%` 时无法双击预览](https://github.com/siyuan-note/siyuan/issues/13388)
* [在 Linux 上使用鼠标中键关闭标签页会触发粘贴](https://github.com/siyuan-note/siyuan/pull/13395)
* [改进设置外观优先级](https://github.com/siyuan-note/siyuan/issues/13404)
### 修复缺陷
* [移动端数据历史中的文档在下滑时会被关闭](https://github.com/siyuan-note/siyuan/issues/13347)
* [点击行级标签后未找到结果](https://github.com/siyuan-note/siyuan/issues/13351)
* [HTML 标签搜索转义问题](https://github.com/siyuan-note/siyuan/issues/13354)
* [导出到 .docx 的有序列表序号不正确](https://github.com/siyuan-note/siyuan/issues/13365)
* [以 `.` 结尾的文件名会被视为缺失资源](https://github.com/siyuan-note/siyuan/issues/13368)
* [完全手动同步模式导致数据冲突](https://github.com/siyuan-note/siyuan/issues/13387)
### 开发者
* [改进选择块或表格单元格后 `Backspace` `Delete` `Tab` `Shift+Tab` 的操作](https://github.com/siyuan-note/siyuan/issues/13027)
* [添加内核 API `/api/filetree/moveDocsByID`](https://github.com/siyuan-note/siyuan/issues/13247)
* [移除编辑器输入控制台日志](https://github.com/siyuan-note/siyuan/issues/13346)
* [为 Protyle 添加 `disable` 和 `enable` 方法](https://github.com/siyuan-note/siyuan/issues/13391)
## 下载
* [B3log](https://b3log.org/siyuan/download.html)
* [GitHub](https://github.com/siyuan-note/siyuan/releases)

View file

@ -43,7 +43,7 @@ const windowStatePath = path.join(confDir, "windowState.json");
let bootWindow;
let latestActiveWindow;
let firstOpen = false;
let workspaces = []; // workspaceDir, id, browserWindow, tray
let workspaces = []; // workspaceDir, id, browserWindow, tray, hideShortcut
let kernelPort = 6806;
let resetWindowStateOnRestart = false;
@ -1098,6 +1098,12 @@ app.whenReady().then(() => {
if (!data.hotkeys || data.hotkeys.length === 0) {
return;
}
workspaces.find(workspaceItem => {
if (event.sender.id === workspaceItem.browserWindow.webContents.id) {
workspaceItem.hotkeys = data.hotkeys;
return true;
}
});
data.hotkeys.forEach((item, index) => {
const shortcut = hotKey2Electron(item);
if (!shortcut) {
@ -1111,11 +1117,19 @@ app.whenReady().then(() => {
let currentWorkspace;
const currentWebContentsId = (latestActiveWindow && !latestActiveWindow.isDestroyed()) ? latestActiveWindow.webContents.id : undefined;
workspaces.find(workspaceItem => {
if ((currentWebContentsId || event.sender.id) === workspaceItem.browserWindow.webContents.id) {
if (currentWebContentsId === workspaceItem.browserWindow.webContents.id && workspaceItem.hotkeys[0] === item) {
currentWorkspace = workspaceItem;
return true;
}
});
if (!currentWorkspace) {
workspaces.find(workspaceItem => {
if (workspaceItem.hotkeys[0] === item && event.sender.id === workspaceItem.browserWindow.webContents.id) {
currentWorkspace = workspaceItem;
return true;
}
});
}
if (!currentWorkspace) {
return;
}

View file

@ -6,7 +6,7 @@
"id": "20201210233038-3xr19g5",
"title": "Conversion of Document and Heading",
"type": "doc",
"updated": "20241018105907"
"updated": "20241214173509"
},
"Children": [
{
@ -135,7 +135,8 @@
"Type": "NodeHeading",
"HeadingLevel": 2,
"Properties": {
"id": "20210104091550-5sh49go"
"id": "20210104091550-5sh49go",
"updated": "20241214173509"
},
"Children": [
{
@ -152,12 +153,13 @@
"ID": "20210104091550-rbetlgr",
"Type": "NodeParagraph",
"Properties": {
"id": "20210104091550-rbetlgr"
"id": "20210104091550-rbetlgr",
"updated": "20241214173509"
},
"Children": [
{
"Type": "NodeText",
"Data": "In the doc tree, select the document that needs to be converted into a heading block, and then drag it to the position to be inserted in the editor tab. There are two situations here:"
"Data": "In the document tree, select the document you want to convert to a heading block, then hold down the Alt key and drag it to the position in the editor tab where you want to insert it. There are two situations here:"
}
]
},

View file

@ -6,7 +6,7 @@
"id": "20201204184532-3qm9l8n",
"title": "Template snippet",
"type": "doc",
"updated": "20241008202623"
"updated": "20241213215714"
},
"Children": [
{
@ -57,7 +57,7 @@
"HeadingLevel": 2,
"Properties": {
"id": "20210104091444-jy56z0p",
"updated": "20241008202623"
"updated": "20241213215714"
},
"Children": [
{
@ -215,7 +215,7 @@
"ListData": {},
"Properties": {
"id": "20210104091444-mwbvc9m",
"updated": "20240921122726"
"updated": "20241213215714"
},
"Children": [
{
@ -429,7 +429,7 @@
"Type": "NodeParagraph",
"Properties": {
"id": "20210131162138-7ufhbho",
"updated": "20240403150956"
"updated": "20241213215545"
},
"Children": [
{
@ -443,7 +443,33 @@
},
{
"Type": "NodeText",
"Data": ": This function is used to query the database, and the return value is a list of blocks, please refer to the example below"
"Data": ": This function is used to query the database, and the return value is a list of blocks"
}
]
},
{
"ID": "20241213215602-dk7iion",
"Type": "NodeCodeBlock",
"IsFencedCodeBlock": true,
"Properties": {
"id": "20241213215602-dk7iion",
"updated": "20241213215602"
},
"Children": [
{
"Type": "NodeCodeBlockFenceOpenMarker",
"Data": "```"
},
{
"Type": "NodeCodeBlockFenceInfoMarker"
},
{
"Type": "NodeCodeBlockCode",
"Data": ".action{$today := now | date \"20060102150405\"}\n.action{$blocks :=queryBlocks \"SELECT * FROM blocks WHERE content LIKE '?' AND updated \u003e '?' LIMIT ?\" \"%foo%\" $today \"3\"}\n"
},
{
"Type": "NodeCodeBlockFenceCloseMarker",
"Data": "```"
}
]
}
@ -466,7 +492,7 @@
"Type": "NodeParagraph",
"Properties": {
"id": "20210504093232-6wxl589",
"updated": "20240403151002"
"updated": "20241213215547"
},
"Children": [
{
@ -480,7 +506,375 @@
},
{
"Type": "NodeText",
"Data": ": This function is used to query the database, and the return value is a list of spans, please refer to the example below"
"Data": ": This function is used to query the database, and the return value is a list of spans"
}
]
},
{
"ID": "20241213215606-a1mq6zf",
"Type": "NodeCodeBlock",
"IsFencedCodeBlock": true,
"Properties": {
"id": "20241213215606-a1mq6zf",
"updated": "20241213215606"
},
"Children": [
{
"Type": "NodeCodeBlockFenceOpenMarker",
"Data": "```"
},
{
"Type": "NodeCodeBlockFenceInfoMarker"
},
{
"Type": "NodeCodeBlockCode",
"Data": " .action{querySQL \"SELECT * FROM spans LIMIT ?\" \"3\"}\n"
},
{
"Type": "NodeCodeBlockFenceCloseMarker",
"Data": "```"
}
]
}
]
},
{
"ID": "20241213214939-he4wmcd",
"Type": "NodeListItem",
"ListData": {
"BulletChar": 42,
"Marker": "Kg=="
},
"Properties": {
"id": "20241213214939-he4wmcd",
"updated": "20241213214939"
},
"Children": [
{
"ID": "20241213214939-hrshqgv",
"Type": "NodeParagraph",
"Properties": {
"id": "20241213214939-hrshqgv",
"updated": "20241213215550"
},
"Children": [
{
"Type": "NodeText",
"Data": ""
},
{
"Type": "NodeTextMark",
"TextMarkType": "code",
"TextMarkTextContent": "querySQL"
},
{
"Type": "NodeText",
"Data": ": This function is used to query the database and the return value is a result set"
}
]
},
{
"ID": "20241213215554-rox3jk7",
"Type": "NodeCodeBlock",
"IsFencedCodeBlock": true,
"Properties": {
"id": "20241213215554-rox3jk7",
"updated": "20241213215554"
},
"Children": [
{
"Type": "NodeCodeBlockFenceOpenMarker",
"Data": "```"
},
{
"Type": "NodeCodeBlockFenceInfoMarker"
},
{
"Type": "NodeCodeBlockCode",
"Data": " .action{querySQL \"SELECT * FROM refs LIMIT 3\"}\n"
},
{
"Type": "NodeCodeBlockFenceCloseMarker",
"Data": "```"
}
]
}
]
},
{
"ID": "20241212102027-j293sei",
"Type": "NodeListItem",
"ListData": {
"BulletChar": 42,
"Marker": "Kg=="
},
"Properties": {
"id": "20241212102027-j293sei",
"updated": "20241213215714"
},
"Children": [
{
"ID": "20241212102028-6c8tlnu",
"Type": "NodeParagraph",
"Properties": {
"id": "20241212102028-6c8tlnu",
"updated": "20241212163902"
},
"Children": [
{
"Type": "NodeText",
"Data": ""
},
{
"Type": "NodeTextMark",
"TextMarkType": "code",
"TextMarkTextContent": "statBlock"
},
{
"Type": "NodeText",
"Data": ": This function is used to count the block content"
}
]
},
{
"ID": "20241213215714-u0j3i4k",
"Type": "NodeCodeBlock",
"IsFencedCodeBlock": true,
"Properties": {
"id": "20241213215714-u0j3i4k",
"updated": "20241213215714"
},
"Children": [
{
"Type": "NodeCodeBlockFenceOpenMarker",
"Data": "```"
},
{
"Type": "NodeCodeBlockFenceInfoMarker"
},
{
"Type": "NodeCodeBlockCode",
"Data": ".action{ (statBlock .id).RuneCount} .action{ (statBlock .id).WordCount}\n"
},
{
"Type": "NodeCodeBlockFenceCloseMarker",
"Data": "```"
}
]
},
{
"ID": "20241212102028-4ew89te",
"Type": "NodeList",
"ListData": {},
"Properties": {
"id": "20241212102028-4ew89te",
"updated": "20241212102028"
},
"Children": [
{
"ID": "20241212102028-wai3afs",
"Type": "NodeListItem",
"ListData": {
"BulletChar": 42,
"Marker": "Kg=="
},
"Properties": {
"id": "20241212102028-wai3afs",
"updated": "20241212102028"
},
"Children": [
{
"ID": "20241212102028-wmtwngj",
"Type": "NodeParagraph",
"Properties": {
"id": "20241212102028-wmtwngj",
"updated": "20241212102028"
},
"Children": [
{
"Type": "NodeText",
"Data": "RuneCount"
}
]
}
]
},
{
"ID": "20241212102028-r9k7ody",
"Type": "NodeListItem",
"ListData": {
"BulletChar": 42,
"Marker": "Kg=="
},
"Properties": {
"id": "20241212102028-r9k7ody",
"updated": "20241212102028"
},
"Children": [
{
"ID": "20241212102028-le5lpgo",
"Type": "NodeParagraph",
"Properties": {
"id": "20241212102028-le5lpgo",
"updated": "20241212102028"
},
"Children": [
{
"Type": "NodeText",
"Data": "WordCount"
}
]
}
]
},
{
"ID": "20241212102028-3egh1dg",
"Type": "NodeListItem",
"ListData": {
"BulletChar": 42,
"Marker": "Kg=="
},
"Properties": {
"id": "20241212102028-3egh1dg",
"updated": "20241212102028"
},
"Children": [
{
"ID": "20241212102028-pijmoea",
"Type": "NodeParagraph",
"Properties": {
"id": "20241212102028-pijmoea",
"updated": "20241212102028"
},
"Children": [
{
"Type": "NodeText",
"Data": "LinkCount"
}
]
}
]
},
{
"ID": "20241212102028-qu6f5tc",
"Type": "NodeListItem",
"ListData": {
"BulletChar": 42,
"Marker": "Kg=="
},
"Properties": {
"id": "20241212102028-qu6f5tc",
"updated": "20241212102028"
},
"Children": [
{
"ID": "20241212102028-8yj65um",
"Type": "NodeParagraph",
"Properties": {
"id": "20241212102028-8yj65um",
"updated": "20241212102028"
},
"Children": [
{
"Type": "NodeText",
"Data": "ImageCount"
}
]
}
]
},
{
"ID": "20241212102028-8ivthzb",
"Type": "NodeListItem",
"ListData": {
"BulletChar": 42,
"Marker": "Kg=="
},
"Properties": {
"id": "20241212102028-8ivthzb",
"updated": "20241212102028"
},
"Children": [
{
"ID": "20241212102028-p8nmm3a",
"Type": "NodeParagraph",
"Properties": {
"id": "20241212102028-p8nmm3a",
"updated": "20241212102028"
},
"Children": [
{
"Type": "NodeText",
"Data": "RefCount"
}
]
}
]
},
{
"ID": "20241212102028-frusted",
"Type": "NodeListItem",
"ListData": {
"BulletChar": 42,
"Marker": "Kg=="
},
"Properties": {
"id": "20241212102028-frusted",
"updated": "20241212102028"
},
"Children": [
{
"ID": "20241212102028-h58utua",
"Type": "NodeParagraph",
"Properties": {
"id": "20241212102028-h58utua",
"updated": "20241212102028"
},
"Children": [
{
"Type": "NodeText",
"Data": "BlockCount"
}
]
}
]
}
]
}
]
},
{
"ID": "20241212163841-3yjhffn",
"Type": "NodeListItem",
"ListData": {
"BulletChar": 42,
"Marker": "Kg=="
},
"Properties": {
"id": "20241212163841-3yjhffn",
"updated": "20241212163918"
},
"Children": [
{
"ID": "20241212163841-d8t3qxb",
"Type": "NodeParagraph",
"Properties": {
"id": "20241212163841-d8t3qxb",
"updated": "20241212163918"
},
"Children": [
{
"Type": "NodeText",
"Data": ""
},
{
"Type": "NodeTextMark",
"TextMarkType": "code",
"TextMarkTextContent": "runeLen"
},
{
"Type": "NodeText",
"Data": ": This function is used to return the string length"
}
]
}
@ -857,64 +1251,6 @@
}
]
},
{
"ID": "20210604111714-yc1e5gj",
"Type": "NodeParagraph",
"Properties": {
"id": "20210604111714-yc1e5gj",
"updated": "20210604111715"
},
"Children": [
{
"Type": "NodeTextMark",
"TextMarkType": "code",
"TextMarkTextContent": "queryBlocks"
},
{
"Type": "NodeText",
"Data": " and "
},
{
"Type": "NodeTextMark",
"TextMarkType": "code",
"TextMarkTextContent": "querySpans"
},
{
"Type": "NodeText",
"Data": " support variable parameter lists similar to SQL prepared statements to facilitate the input of parameters:"
}
]
},
{
"ID": "20210604111750-c3sm3jr",
"Type": "NodeCodeBlock",
"IsFencedCodeBlock": true,
"Properties": {
"id": "20210604111750-c3sm3jr",
"updated": "20210604111632"
},
"Children": [
{
"Type": "NodeCodeBlockFenceOpenMarker",
"Data": "```"
},
{
"Type": "NodeCodeBlockFenceInfoMarker"
},
{
"ID": "20220307092214-3yza25q",
"Type": "NodeCodeBlockCode",
"Data": ".action{$today := now | date \"20060102150405\"}\n.action{$blocks :=queryBlocks \"SELECT * FROM blocks WHERE content LIKE '?' AND updated \u003e '?' LIMIT ?\" \"%foo%\" $today \"3\"}\n",
"Properties": {
"id": "20220307092214-3yza25q"
}
},
{
"Type": "NodeCodeBlockFenceCloseMarker",
"Data": "```"
}
]
},
{
"ID": "20210104091444-4re70bp",
"Type": "NodeHeading",

View file

@ -6,7 +6,7 @@
"id": "20201210103036-1x3vm8t",
"title": "文档块和标题块的转换",
"type": "doc",
"updated": "20241018104534"
"updated": "20241214173542"
},
"Children": [
{
@ -135,7 +135,8 @@
"Type": "NodeHeading",
"HeadingLevel": 2,
"Properties": {
"id": "20210104090801-tojy8es"
"id": "20210104090801-tojy8es",
"updated": "20241214173542"
},
"Children": [
{
@ -152,12 +153,13 @@
"ID": "20210104090801-q0rn7el",
"Type": "NodeParagraph",
"Properties": {
"id": "20210104090801-q0rn7el"
"id": "20210104090801-q0rn7el",
"updated": "20241214173542"
},
"Children": [
{
"Type": "NodeText",
"Data": "在文档树上选择需要转换为标题块的文档,然后将其拖拽到编辑器页签中需要插入的位置。这里有两种情况:"
"Data": "在文档树上选择需要转换为标题块的文档,然后按住 Alt 键将其拖拽到编辑器页签中需要插入的位置。这里有两种情况:"
}
]
},

View file

@ -6,7 +6,7 @@
"id": "20201204181006-7bkppue",
"title": "模板片段",
"type": "doc",
"updated": "20241008201610"
"updated": "20241213215655"
},
"Children": [
{
@ -69,7 +69,7 @@
"HeadingLevel": 2,
"Properties": {
"id": "20210104091309-fhb549c",
"updated": "20241008201610"
"updated": "20241213215655"
},
"Children": [
{
@ -237,7 +237,7 @@
"ListData": {},
"Properties": {
"id": "20210104091309-gjkg3u5",
"updated": "20240921122737"
"updated": "20241213215655"
},
"Children": [
{
@ -451,7 +451,7 @@
"Type": "NodeParagraph",
"Properties": {
"id": "20210131155558-ghlchbx",
"updated": "20240403150712"
"updated": "20241213215236"
},
"Children": [
{
@ -465,7 +465,33 @@
},
{
"Type": "NodeText",
"Data": "​:该函数用于查询数据库,返回值为 blocks 列表,请参考下面的例子"
"Data": "​:该函数用于查询数据库,返回值为 blocks 列表"
}
]
},
{
"ID": "20210604111446-p6vadfc",
"Type": "NodeCodeBlock",
"IsFencedCodeBlock": true,
"Properties": {
"id": "20210604111446-p6vadfc",
"updated": "20241213215118"
},
"Children": [
{
"Type": "NodeCodeBlockFenceOpenMarker",
"Data": "```"
},
{
"Type": "NodeCodeBlockFenceInfoMarker"
},
{
"Type": "NodeCodeBlockCode",
"Data": ".action{$today := now | date \"20060102150405\"}\n.action{$blocks :=queryBlocks \"SELECT * FROM blocks WHERE content LIKE '?' AND updated \u003e '?' LIMIT ?\" \"%foo%\" $today \"3\"}\n"
},
{
"Type": "NodeCodeBlockFenceCloseMarker",
"Data": "```"
}
]
}
@ -480,7 +506,7 @@
},
"Properties": {
"id": "20210504093313-4aoyxd0",
"updated": "20240403150838"
"updated": "20241213215416"
},
"Children": [
{
@ -488,7 +514,7 @@
"Type": "NodeParagraph",
"Properties": {
"id": "20210504093313-ya53o58",
"updated": "20240403150838"
"updated": "20241213215302"
},
"Children": [
{
@ -502,7 +528,375 @@
},
{
"Type": "NodeText",
"Data": "​:该函数用于查询数据库,返回值为 spans 列表,请参考下面的例子"
"Data": "​:该函数用于查询数据库,返回值为 spans 列表"
}
]
},
{
"ID": "20241213215337-rhkoc8k",
"Type": "NodeCodeBlock",
"IsFencedCodeBlock": true,
"Properties": {
"id": "20241213215337-rhkoc8k",
"updated": "20241213215416"
},
"Children": [
{
"Type": "NodeCodeBlockFenceOpenMarker",
"Data": "```"
},
{
"Type": "NodeCodeBlockFenceInfoMarker"
},
{
"Type": "NodeCodeBlockCode",
"Data": " .action{querySQL \"SELECT * FROM spans LIMIT ?\" \"3\"}\n"
},
{
"Type": "NodeCodeBlockFenceCloseMarker",
"Data": "```"
}
]
}
]
},
{
"ID": "20241213214733-ro4xcjm",
"Type": "NodeListItem",
"ListData": {
"BulletChar": 42,
"Marker": "Kg=="
},
"Properties": {
"id": "20241213214733-ro4xcjm",
"updated": "20241213215421"
},
"Children": [
{
"ID": "20241213214733-73dpip9",
"Type": "NodeParagraph",
"Properties": {
"id": "20241213214733-73dpip9",
"updated": "20241213215139"
},
"Children": [
{
"Type": "NodeText",
"Data": ""
},
{
"Type": "NodeTextMark",
"TextMarkType": "code",
"TextMarkTextContent": "querySQL"
},
{
"Type": "NodeText",
"Data": "​:该函数用于查询数据库,返回值为结果集"
}
]
},
{
"ID": "20241213215240-f33bsqs",
"Type": "NodeCodeBlock",
"IsFencedCodeBlock": true,
"Properties": {
"id": "20241213215240-f33bsqs",
"updated": "20241213215421"
},
"Children": [
{
"Type": "NodeCodeBlockFenceOpenMarker",
"Data": "```"
},
{
"Type": "NodeCodeBlockFenceInfoMarker"
},
{
"Type": "NodeCodeBlockCode",
"Data": " .action{querySQL \"SELECT * FROM refs LIMIT 3\"}\n"
},
{
"Type": "NodeCodeBlockFenceCloseMarker",
"Data": "```"
}
]
}
]
},
{
"ID": "20241212101708-0euo6is",
"Type": "NodeListItem",
"ListData": {
"BulletChar": 42,
"Marker": "Kg=="
},
"Properties": {
"id": "20241212101708-0euo6is",
"updated": "20241213215655"
},
"Children": [
{
"ID": "20241212101708-llv0ngj",
"Type": "NodeParagraph",
"Properties": {
"id": "20241212101708-llv0ngj",
"updated": "20241212163723"
},
"Children": [
{
"Type": "NodeText",
"Data": ""
},
{
"Type": "NodeTextMark",
"TextMarkType": "code",
"TextMarkTextContent": "statBlock"
},
{
"Type": "NodeText",
"Data": "​:该函数用于统计块内容"
}
]
},
{
"ID": "20241212101810-6ffociu",
"Type": "NodeCodeBlock",
"IsFencedCodeBlock": true,
"Properties": {
"id": "20241212101810-6ffociu",
"updated": "20241213215655"
},
"Children": [
{
"Type": "NodeCodeBlockFenceOpenMarker",
"Data": "```"
},
{
"Type": "NodeCodeBlockFenceInfoMarker"
},
{
"Type": "NodeCodeBlockCode",
"Data": ".action{ (statBlock .id).RuneCount} .action{ (statBlock .id).WordCount}\n"
},
{
"Type": "NodeCodeBlockFenceCloseMarker",
"Data": "```"
}
]
},
{
"ID": "20241212101821-kf6un09",
"Type": "NodeList",
"ListData": {},
"Properties": {
"id": "20241212101821-kf6un09",
"updated": "20241212101821"
},
"Children": [
{
"ID": "20241212101821-1qt2j1i",
"Type": "NodeListItem",
"ListData": {
"BulletChar": 42,
"Marker": "Kg=="
},
"Properties": {
"id": "20241212101821-1qt2j1i",
"updated": "20241212101821"
},
"Children": [
{
"ID": "20241212101821-mkxql2a",
"Type": "NodeParagraph",
"Properties": {
"id": "20241212101821-mkxql2a",
"updated": "20241212101821"
},
"Children": [
{
"Type": "NodeText",
"Data": "RuneCount"
}
]
}
]
},
{
"ID": "20241212101821-kok8u5t",
"Type": "NodeListItem",
"ListData": {
"BulletChar": 42,
"Marker": "Kg=="
},
"Properties": {
"id": "20241212101821-kok8u5t",
"updated": "20241212101821"
},
"Children": [
{
"ID": "20241212101821-7gsgfqo",
"Type": "NodeParagraph",
"Properties": {
"id": "20241212101821-7gsgfqo",
"updated": "20241212101821"
},
"Children": [
{
"Type": "NodeText",
"Data": "WordCount"
}
]
}
]
},
{
"ID": "20241212101821-jxbirdz",
"Type": "NodeListItem",
"ListData": {
"BulletChar": 42,
"Marker": "Kg=="
},
"Properties": {
"id": "20241212101821-jxbirdz",
"updated": "20241212101821"
},
"Children": [
{
"ID": "20241212101821-mep8mwc",
"Type": "NodeParagraph",
"Properties": {
"id": "20241212101821-mep8mwc",
"updated": "20241212101821"
},
"Children": [
{
"Type": "NodeText",
"Data": "LinkCount"
}
]
}
]
},
{
"ID": "20241212101821-8dho5yg",
"Type": "NodeListItem",
"ListData": {
"BulletChar": 42,
"Marker": "Kg=="
},
"Properties": {
"id": "20241212101821-8dho5yg",
"updated": "20241212101821"
},
"Children": [
{
"ID": "20241212101821-hriiyll",
"Type": "NodeParagraph",
"Properties": {
"id": "20241212101821-hriiyll",
"updated": "20241212101821"
},
"Children": [
{
"Type": "NodeText",
"Data": "ImageCount"
}
]
}
]
},
{
"ID": "20241212101821-mrg92au",
"Type": "NodeListItem",
"ListData": {
"BulletChar": 42,
"Marker": "Kg=="
},
"Properties": {
"id": "20241212101821-mrg92au",
"updated": "20241212101821"
},
"Children": [
{
"ID": "20241212101821-ovwsmzs",
"Type": "NodeParagraph",
"Properties": {
"id": "20241212101821-ovwsmzs",
"updated": "20241212101821"
},
"Children": [
{
"Type": "NodeText",
"Data": "RefCount"
}
]
}
]
},
{
"ID": "20241212101821-kq0fb53",
"Type": "NodeListItem",
"ListData": {
"BulletChar": 42,
"Marker": "Kg=="
},
"Properties": {
"id": "20241212101821-kq0fb53",
"updated": "20241212101821"
},
"Children": [
{
"ID": "20241212101821-6k4evcn",
"Type": "NodeParagraph",
"Properties": {
"id": "20241212101821-6k4evcn",
"updated": "20241212101821"
},
"Children": [
{
"Type": "NodeText",
"Data": "BlockCount"
}
]
}
]
}
]
}
]
},
{
"ID": "20241212163704-qmyz1ss",
"Type": "NodeListItem",
"ListData": {
"BulletChar": 42,
"Marker": "Kg=="
},
"Properties": {
"id": "20241212163704-qmyz1ss",
"updated": "20241212163739"
},
"Children": [
{
"ID": "20241212163704-i9p11h6",
"Type": "NodeParagraph",
"Properties": {
"id": "20241212163704-i9p11h6",
"updated": "20241212163739"
},
"Children": [
{
"Type": "NodeText",
"Data": ""
},
{
"Type": "NodeTextMark",
"TextMarkType": "code",
"TextMarkTextContent": "runeLen"
},
{
"Type": "NodeText",
"Data": "​:该函数用于返回字符串长度"
}
]
}
@ -879,64 +1273,6 @@
}
]
},
{
"ID": "20210604111213-774ll6c",
"Type": "NodeParagraph",
"Properties": {
"id": "20210604111213-774ll6c",
"updated": "20210604111557"
},
"Children": [
{
"Type": "NodeTextMark",
"TextMarkType": "code",
"TextMarkTextContent": "queryBlocks"
},
{
"Type": "NodeText",
"Data": " 和 "
},
{
"Type": "NodeTextMark",
"TextMarkType": "code",
"TextMarkTextContent": "querySpans"
},
{
"Type": "NodeText",
"Data": " 支持类似 SQL 预编译语句的变参列表,方便传入参数:"
}
]
},
{
"ID": "20210604111446-p6vadfc",
"Type": "NodeCodeBlock",
"IsFencedCodeBlock": true,
"Properties": {
"id": "20210604111446-p6vadfc",
"updated": "20210604111632"
},
"Children": [
{
"Type": "NodeCodeBlockFenceOpenMarker",
"Data": "```"
},
{
"Type": "NodeCodeBlockFenceInfoMarker"
},
{
"ID": "20220307091943-diuexe8",
"Type": "NodeCodeBlockCode",
"Data": ".action{$today := now | date \"20060102150405\"}\n.action{$blocks :=queryBlocks \"SELECT * FROM blocks WHERE content LIKE '?' AND updated \u003e '?' LIMIT ?\" \"%foo%\" $today \"3\"}\n",
"Properties": {
"id": "20220307091943-diuexe8"
}
},
{
"Type": "NodeCodeBlockFenceCloseMarker",
"Data": "```"
}
]
},
{
"ID": "20210104091309-h71ogwf",
"Type": "NodeHeading",

View file

@ -6,7 +6,7 @@
"id": "20211226120422-bkzsd2e",
"title": "文檔塊和標題塊的轉換",
"type": "doc",
"updated": "20241018105510"
"updated": "20241214173531"
},
"Children": [
{
@ -121,7 +121,7 @@
"HeadingLevel": 2,
"Properties": {
"id": "20211226120500-5g12nsc",
"updated": "20211225234143"
"updated": "20241214173531"
},
"Children": [
{
@ -135,12 +135,12 @@
"Type": "NodeParagraph",
"Properties": {
"id": "20211226120500-me21dop",
"updated": "20211226160825"
"updated": "20241214173531"
},
"Children": [
{
"Type": "NodeText",
"Data": "在文檔樹上選擇需要轉換為標題塊的文檔,然後將其拖拽到編輯器頁中需要插入的位置。這裡有兩種情況:"
"Data": "在文檔樹上選擇需要轉換為標題塊的文檔,然後按住 Alt 鍵將其拖拽到編輯器頁中需要插入的位置。這裡有兩種情況:"
}
]
},

View file

@ -6,7 +6,7 @@
"id": "20211226123004-dplpw0o",
"title": "範本片段",
"type": "doc",
"updated": "20241008201617"
"updated": "20241213215707"
},
"Children": [
{
@ -53,7 +53,7 @@
"HeadingLevel": 2,
"Properties": {
"id": "20211226123024-eyagqur",
"updated": "20241008201617"
"updated": "20241213215707"
},
"Children": [
{
@ -208,7 +208,7 @@
"ListData": {},
"Properties": {
"id": "20211226123024-pjvw31z",
"updated": "20240921122732"
"updated": "20241213215707"
},
"Children": [
{
@ -422,7 +422,7 @@
"Type": "NodeParagraph",
"Properties": {
"id": "20211226123024-vxjdsno",
"updated": "20240403150942"
"updated": "20241213215430"
},
"Children": [
{
@ -436,7 +436,33 @@
},
{
"Type": "NodeText",
"Data": "​:該函數用於查詢資料庫,返回值為 blocks 列表,請參考下面的例子"
"Data": "​:該函數用於查詢資料庫,返回值為 blocks 列表"
}
]
},
{
"ID": "20241213215519-mfgruqs",
"Type": "NodeCodeBlock",
"IsFencedCodeBlock": true,
"Properties": {
"id": "20241213215519-mfgruqs",
"updated": "20241213215519"
},
"Children": [
{
"Type": "NodeCodeBlockFenceOpenMarker",
"Data": "```"
},
{
"Type": "NodeCodeBlockFenceInfoMarker"
},
{
"Type": "NodeCodeBlockCode",
"Data": ".action{$today := now | date \"20060102150405\"}\n.action{$blocks :=queryBlocks \"SELECT * FROM blocks WHERE content LIKE '?' AND updated \u003e '?' LIMIT ?\" \"%foo%\" $today \"3\"}\n"
},
{
"Type": "NodeCodeBlockFenceCloseMarker",
"Data": "```"
}
]
}
@ -459,7 +485,7 @@
"Type": "NodeParagraph",
"Properties": {
"id": "20211226123024-4qwyf3w",
"updated": "20240403150944"
"updated": "20241213215431"
},
"Children": [
{
@ -473,7 +499,375 @@
},
{
"Type": "NodeText",
"Data": "​:該函數用於查詢資料庫,返回值為 spans 列表,請參考下面的例子"
"Data": "​:該函數用於查詢資料庫,返回值為 spans 列表"
}
]
},
{
"ID": "20241213215524-9an3khb",
"Type": "NodeCodeBlock",
"IsFencedCodeBlock": true,
"Properties": {
"id": "20241213215524-9an3khb",
"updated": "20241213215524"
},
"Children": [
{
"Type": "NodeCodeBlockFenceOpenMarker",
"Data": "```"
},
{
"Type": "NodeCodeBlockFenceInfoMarker"
},
{
"Type": "NodeCodeBlockCode",
"Data": " .action{querySQL \"SELECT * FROM spans LIMIT ?\" \"3\"}\n"
},
{
"Type": "NodeCodeBlockFenceCloseMarker",
"Data": "```"
}
]
}
]
},
{
"ID": "20241213214930-4votv1n",
"Type": "NodeListItem",
"ListData": {
"BulletChar": 42,
"Marker": "Kg=="
},
"Properties": {
"id": "20241213214930-4votv1n",
"updated": "20241213214930"
},
"Children": [
{
"ID": "20241213214930-dogaks1",
"Type": "NodeParagraph",
"Properties": {
"id": "20241213214930-dogaks1",
"updated": "20241213215433"
},
"Children": [
{
"Type": "NodeText",
"Data": ""
},
{
"Type": "NodeTextMark",
"TextMarkType": "code",
"TextMarkTextContent": "querySQL"
},
{
"Type": "NodeText",
"Data": "​:函數用於查詢資料庫,傳回值為結果集"
}
]
},
{
"ID": "20241213215530-f2iwrjz",
"Type": "NodeCodeBlock",
"IsFencedCodeBlock": true,
"Properties": {
"id": "20241213215530-f2iwrjz",
"updated": "20241213215530"
},
"Children": [
{
"Type": "NodeCodeBlockFenceOpenMarker",
"Data": "```"
},
{
"Type": "NodeCodeBlockFenceInfoMarker"
},
{
"Type": "NodeCodeBlockCode",
"Data": " .action{querySQL \"SELECT * FROM refs LIMIT 3\"}\n"
},
{
"Type": "NodeCodeBlockFenceCloseMarker",
"Data": "```"
}
]
}
]
},
{
"ID": "20241212102019-gassjqt",
"Type": "NodeListItem",
"ListData": {
"BulletChar": 42,
"Marker": "Kg=="
},
"Properties": {
"id": "20241212102019-gassjqt",
"updated": "20241213215707"
},
"Children": [
{
"ID": "20241212102021-54wv84h",
"Type": "NodeParagraph",
"Properties": {
"id": "20241212102021-54wv84h",
"updated": "20241212164010"
},
"Children": [
{
"Type": "NodeText",
"Data": ""
},
{
"Type": "NodeTextMark",
"TextMarkType": "code",
"TextMarkTextContent": "statBlock"
},
{
"Type": "NodeText",
"Data": "​:此函數用於統計區塊內容"
}
]
},
{
"ID": "20241213215707-lxpmd6b",
"Type": "NodeCodeBlock",
"IsFencedCodeBlock": true,
"Properties": {
"id": "20241213215707-lxpmd6b",
"updated": "20241213215707"
},
"Children": [
{
"Type": "NodeCodeBlockFenceOpenMarker",
"Data": "```"
},
{
"Type": "NodeCodeBlockFenceInfoMarker"
},
{
"Type": "NodeCodeBlockCode",
"Data": ".action{ (statBlock .id).RuneCount} .action{ (statBlock .id).WordCount}\n"
},
{
"Type": "NodeCodeBlockFenceCloseMarker",
"Data": "```"
}
]
},
{
"ID": "20241212102021-vj2zyyl",
"Type": "NodeList",
"ListData": {},
"Properties": {
"id": "20241212102021-vj2zyyl",
"updated": "20241212102021"
},
"Children": [
{
"ID": "20241212102021-92gy6t3",
"Type": "NodeListItem",
"ListData": {
"BulletChar": 42,
"Marker": "Kg=="
},
"Properties": {
"id": "20241212102021-92gy6t3",
"updated": "20241212102021"
},
"Children": [
{
"ID": "20241212102021-d3lo8xu",
"Type": "NodeParagraph",
"Properties": {
"id": "20241212102021-d3lo8xu",
"updated": "20241212102021"
},
"Children": [
{
"Type": "NodeText",
"Data": "RuneCount"
}
]
}
]
},
{
"ID": "20241212102021-n8ufevw",
"Type": "NodeListItem",
"ListData": {
"BulletChar": 42,
"Marker": "Kg=="
},
"Properties": {
"id": "20241212102021-n8ufevw",
"updated": "20241212102021"
},
"Children": [
{
"ID": "20241212102021-omvwh9v",
"Type": "NodeParagraph",
"Properties": {
"id": "20241212102021-omvwh9v",
"updated": "20241212102021"
},
"Children": [
{
"Type": "NodeText",
"Data": "WordCount"
}
]
}
]
},
{
"ID": "20241212102021-s12eoe7",
"Type": "NodeListItem",
"ListData": {
"BulletChar": 42,
"Marker": "Kg=="
},
"Properties": {
"id": "20241212102021-s12eoe7",
"updated": "20241212102021"
},
"Children": [
{
"ID": "20241212102021-518qvnd",
"Type": "NodeParagraph",
"Properties": {
"id": "20241212102021-518qvnd",
"updated": "20241212102021"
},
"Children": [
{
"Type": "NodeText",
"Data": "LinkCount"
}
]
}
]
},
{
"ID": "20241212102021-dolb6ct",
"Type": "NodeListItem",
"ListData": {
"BulletChar": 42,
"Marker": "Kg=="
},
"Properties": {
"id": "20241212102021-dolb6ct",
"updated": "20241212102021"
},
"Children": [
{
"ID": "20241212102021-dx5rhed",
"Type": "NodeParagraph",
"Properties": {
"id": "20241212102021-dx5rhed",
"updated": "20241212102021"
},
"Children": [
{
"Type": "NodeText",
"Data": "ImageCount"
}
]
}
]
},
{
"ID": "20241212102021-yt0q53w",
"Type": "NodeListItem",
"ListData": {
"BulletChar": 42,
"Marker": "Kg=="
},
"Properties": {
"id": "20241212102021-yt0q53w",
"updated": "20241212102021"
},
"Children": [
{
"ID": "20241212102021-z97tnzw",
"Type": "NodeParagraph",
"Properties": {
"id": "20241212102021-z97tnzw",
"updated": "20241212102021"
},
"Children": [
{
"Type": "NodeText",
"Data": "RefCount"
}
]
}
]
},
{
"ID": "20241212102021-npgihna",
"Type": "NodeListItem",
"ListData": {
"BulletChar": 42,
"Marker": "Kg=="
},
"Properties": {
"id": "20241212102021-npgihna",
"updated": "20241212102021"
},
"Children": [
{
"ID": "20241212102021-80qheu7",
"Type": "NodeParagraph",
"Properties": {
"id": "20241212102021-80qheu7",
"updated": "20241212102021"
},
"Children": [
{
"Type": "NodeText",
"Data": "BlockCount"
}
]
}
]
}
]
}
]
},
{
"ID": "20241212164016-nxteno5",
"Type": "NodeListItem",
"ListData": {
"BulletChar": 42,
"Marker": "Kg=="
},
"Properties": {
"id": "20241212164016-nxteno5",
"updated": "20241212164017"
},
"Children": [
{
"ID": "20241212164016-ot8vo9l",
"Type": "NodeParagraph",
"Properties": {
"id": "20241212164016-ot8vo9l",
"updated": "20241212164017"
},
"Children": [
{
"Type": "NodeText",
"Data": ""
},
{
"Type": "NodeTextMark",
"TextMarkType": "code",
"TextMarkTextContent": "runeLen"
},
{
"Type": "NodeText",
"Data": "​:此函數用於傳回字串長度"
}
]
}
@ -850,64 +1244,6 @@
}
]
},
{
"ID": "20211226123024-5rk6w30",
"Type": "NodeParagraph",
"Properties": {
"id": "20211226123024-5rk6w30",
"updated": "20211228134953"
},
"Children": [
{
"Type": "NodeTextMark",
"TextMarkType": "code",
"TextMarkTextContent": "queryBlocks"
},
{
"Type": "NodeText",
"Data": " 和 "
},
{
"Type": "NodeTextMark",
"TextMarkType": "code",
"TextMarkTextContent": "querySpans"
},
{
"Type": "NodeText",
"Data": " 支援類似 SQL 預編譯語句的變參列表,方便傳入參數:"
}
]
},
{
"ID": "20211226123024-sw1k190",
"Type": "NodeCodeBlock",
"IsFencedCodeBlock": true,
"Properties": {
"id": "20211226123024-sw1k190",
"updated": "20211225221841"
},
"Children": [
{
"Type": "NodeCodeBlockFenceOpenMarker",
"Data": "```"
},
{
"Type": "NodeCodeBlockFenceInfoMarker"
},
{
"ID": "20220307092237-mawslux",
"Type": "NodeCodeBlockCode",
"Data": ".action{$today := now | date \"20060102150405\"}\n.action{$blocks :=queryBlocks \"SELECT * FROM blocks WHERE content LIKE '?' AND updated \u003e '?' LIMIT ?\" \"%foo%\" $today \"3\"}\n",
"Properties": {
"id": "20220307092237-mawslux"
}
},
{
"Type": "NodeCodeBlockFenceCloseMarker",
"Data": "```"
}
]
},
{
"ID": "20211226123024-3wiifft",
"Type": "NodeHeading",

View file

@ -7,7 +7,7 @@
"id": "20240530101000-6x9ivi7",
"title": "テンプレートスニペット",
"type": "doc",
"updated": "20240921123108"
"updated": "20241213215720"
},
"Children": [
{
@ -66,7 +66,7 @@
"Properties": {
"ID": "20240530101000-5nvn9ad",
"id": "20240530101000-pi05yzt",
"updated": "20240921123108"
"updated": "20241213215720"
},
"Children": [
{
@ -226,7 +226,7 @@
"ListData": {},
"Properties": {
"id": "20240530101000-by5hqnb",
"updated": "20240921123108"
"updated": "20241213215720"
},
"Children": [
{
@ -440,7 +440,7 @@
"Type": "NodeParagraph",
"Properties": {
"id": "20240530101000-obb5zo9",
"updated": "20240530101000"
"updated": "20241213215612"
},
"Children": [
{
@ -454,7 +454,33 @@
},
{
"Type": "NodeText",
"Data": ": この関数はデータベースをクエリしてブロックのリストを返します (下記の使用例を参照してください)"
"Data": ": この関数はデータベースをクエリしてブロックのリストを返します"
}
]
},
{
"ID": "20241213215628-5gixpmp",
"Type": "NodeCodeBlock",
"IsFencedCodeBlock": true,
"Properties": {
"id": "20241213215628-5gixpmp",
"updated": "20241213215628"
},
"Children": [
{
"Type": "NodeCodeBlockFenceOpenMarker",
"Data": "```"
},
{
"Type": "NodeCodeBlockFenceInfoMarker"
},
{
"Type": "NodeCodeBlockCode",
"Data": ".action{$today := now | date \"20060102150405\"}\n.action{$blocks :=queryBlocks \"SELECT * FROM blocks WHERE content LIKE '?' AND updated \u003e '?' LIMIT ?\" \"%foo%\" $today \"3\"}\n"
},
{
"Type": "NodeCodeBlockFenceCloseMarker",
"Data": "```"
}
]
}
@ -477,7 +503,7 @@
"Type": "NodeParagraph",
"Properties": {
"id": "20240530101000-4wi25sk",
"updated": "20240530101000"
"updated": "20241213215614"
},
"Children": [
{
@ -491,7 +517,375 @@
},
{
"Type": "NodeText",
"Data": ": この関数はデータベースをクエリしてスパンのリストを返します (下記の使用例を参照してください)"
"Data": ": この関数はデータベースをクエリしてスパンのリストを返します"
}
]
},
{
"ID": "20241213215619-gkqs282",
"Type": "NodeCodeBlock",
"IsFencedCodeBlock": true,
"Properties": {
"id": "20241213215619-gkqs282",
"updated": "20241213215619"
},
"Children": [
{
"Type": "NodeCodeBlockFenceOpenMarker",
"Data": "```"
},
{
"Type": "NodeCodeBlockFenceInfoMarker"
},
{
"Type": "NodeCodeBlockCode",
"Data": " .action{querySQL \"SELECT * FROM spans LIMIT ?\" \"3\"}\n"
},
{
"Type": "NodeCodeBlockFenceCloseMarker",
"Data": "```"
}
]
}
]
},
{
"ID": "20241213215009-qrtc56j",
"Type": "NodeListItem",
"ListData": {
"BulletChar": 42,
"Marker": "Kg=="
},
"Properties": {
"id": "20241213215009-qrtc56j",
"updated": "20241213215009"
},
"Children": [
{
"ID": "20241213215009-fev1gao",
"Type": "NodeParagraph",
"Properties": {
"id": "20241213215009-fev1gao",
"updated": "20241213215617"
},
"Children": [
{
"Type": "NodeText",
"Data": ""
},
{
"Type": "NodeTextMark",
"TextMarkType": "code",
"TextMarkTextContent": "querySQL"
},
{
"Type": "NodeText",
"Data": ": この関数はデータベースへのクエリに使用され、戻り値は結果セットです"
}
]
},
{
"ID": "20241213215633-69ltmv1",
"Type": "NodeCodeBlock",
"IsFencedCodeBlock": true,
"Properties": {
"id": "20241213215633-69ltmv1",
"updated": "20241213215633"
},
"Children": [
{
"Type": "NodeCodeBlockFenceOpenMarker",
"Data": "```"
},
{
"Type": "NodeCodeBlockFenceInfoMarker"
},
{
"Type": "NodeCodeBlockCode",
"Data": " .action{querySQL \"SELECT * FROM refs LIMIT 3\"}\n"
},
{
"Type": "NodeCodeBlockFenceCloseMarker",
"Data": "```"
}
]
}
]
},
{
"ID": "20241212102033-rn9u6t8",
"Type": "NodeListItem",
"ListData": {
"BulletChar": 42,
"Marker": "Kg=="
},
"Properties": {
"id": "20241212102033-rn9u6t8",
"updated": "20241213215720"
},
"Children": [
{
"ID": "20241212102033-d4azb7k",
"Type": "NodeParagraph",
"Properties": {
"id": "20241212102033-d4azb7k",
"updated": "20241212163936"
},
"Children": [
{
"Type": "NodeText",
"Data": ""
},
{
"Type": "NodeTextMark",
"TextMarkType": "code",
"TextMarkTextContent": "statBlock"
},
{
"Type": "NodeText",
"Data": "​:この関数はブロックの内容を数えるために使用されます"
}
]
},
{
"ID": "20241213215720-ueqpsna",
"Type": "NodeCodeBlock",
"IsFencedCodeBlock": true,
"Properties": {
"id": "20241213215720-ueqpsna",
"updated": "20241213215720"
},
"Children": [
{
"Type": "NodeCodeBlockFenceOpenMarker",
"Data": "```"
},
{
"Type": "NodeCodeBlockFenceInfoMarker"
},
{
"Type": "NodeCodeBlockCode",
"Data": ".action{ (statBlock .id).RuneCount} .action{ (statBlock .id).WordCount}\n"
},
{
"Type": "NodeCodeBlockFenceCloseMarker",
"Data": "```"
}
]
},
{
"ID": "20241212102033-ib8fdwx",
"Type": "NodeList",
"ListData": {},
"Properties": {
"id": "20241212102033-ib8fdwx",
"updated": "20241212102033"
},
"Children": [
{
"ID": "20241212102033-lijk8pe",
"Type": "NodeListItem",
"ListData": {
"BulletChar": 42,
"Marker": "Kg=="
},
"Properties": {
"id": "20241212102033-lijk8pe",
"updated": "20241212102033"
},
"Children": [
{
"ID": "20241212102033-ch66lvd",
"Type": "NodeParagraph",
"Properties": {
"id": "20241212102033-ch66lvd",
"updated": "20241212102033"
},
"Children": [
{
"Type": "NodeText",
"Data": "RuneCount"
}
]
}
]
},
{
"ID": "20241212102033-527kxt0",
"Type": "NodeListItem",
"ListData": {
"BulletChar": 42,
"Marker": "Kg=="
},
"Properties": {
"id": "20241212102033-527kxt0",
"updated": "20241212102033"
},
"Children": [
{
"ID": "20241212102033-7iop2es",
"Type": "NodeParagraph",
"Properties": {
"id": "20241212102033-7iop2es",
"updated": "20241212102033"
},
"Children": [
{
"Type": "NodeText",
"Data": "WordCount"
}
]
}
]
},
{
"ID": "20241212102033-zicftlk",
"Type": "NodeListItem",
"ListData": {
"BulletChar": 42,
"Marker": "Kg=="
},
"Properties": {
"id": "20241212102033-zicftlk",
"updated": "20241212102033"
},
"Children": [
{
"ID": "20241212102033-871ju7g",
"Type": "NodeParagraph",
"Properties": {
"id": "20241212102033-871ju7g",
"updated": "20241212102033"
},
"Children": [
{
"Type": "NodeText",
"Data": "LinkCount"
}
]
}
]
},
{
"ID": "20241212102033-2k2gz3h",
"Type": "NodeListItem",
"ListData": {
"BulletChar": 42,
"Marker": "Kg=="
},
"Properties": {
"id": "20241212102033-2k2gz3h",
"updated": "20241212102033"
},
"Children": [
{
"ID": "20241212102033-1ss2d9y",
"Type": "NodeParagraph",
"Properties": {
"id": "20241212102033-1ss2d9y",
"updated": "20241212102033"
},
"Children": [
{
"Type": "NodeText",
"Data": "ImageCount"
}
]
}
]
},
{
"ID": "20241212102033-905g5ga",
"Type": "NodeListItem",
"ListData": {
"BulletChar": 42,
"Marker": "Kg=="
},
"Properties": {
"id": "20241212102033-905g5ga",
"updated": "20241212102033"
},
"Children": [
{
"ID": "20241212102033-k7jgqz4",
"Type": "NodeParagraph",
"Properties": {
"id": "20241212102033-k7jgqz4",
"updated": "20241212102033"
},
"Children": [
{
"Type": "NodeText",
"Data": "RefCount"
}
]
}
]
},
{
"ID": "20241212102033-xbpy9ew",
"Type": "NodeListItem",
"ListData": {
"BulletChar": 42,
"Marker": "Kg=="
},
"Properties": {
"id": "20241212102033-xbpy9ew",
"updated": "20241212102033"
},
"Children": [
{
"ID": "20241212102033-kvquhrn",
"Type": "NodeParagraph",
"Properties": {
"id": "20241212102033-kvquhrn",
"updated": "20241212102033"
},
"Children": [
{
"Type": "NodeText",
"Data": "BlockCount"
}
]
}
]
}
]
}
]
},
{
"ID": "20241212163938-ewajrsj",
"Type": "NodeListItem",
"ListData": {
"BulletChar": 42,
"Marker": "Kg=="
},
"Properties": {
"id": "20241212163938-ewajrsj",
"updated": "20241212163955"
},
"Children": [
{
"ID": "20241212163938-y1al3b9",
"Type": "NodeParagraph",
"Properties": {
"id": "20241212163938-y1al3b9",
"updated": "20241212163955"
},
"Children": [
{
"Type": "NodeText",
"Data": ""
},
{
"Type": "NodeTextMark",
"TextMarkType": "code",
"TextMarkTextContent": "runeLen"
},
{
"Type": "NodeText",
"Data": ": この関数は文字列の長さを返すために使用されます"
}
]
}
@ -868,71 +1262,6 @@
}
]
},
{
"ID": "20240530101000-5twj99z",
"Type": "NodeParagraph",
"Properties": {
"ID": "20240530101000-p45pa4c",
"id": "20240530101000-5twj99z",
"updated": "20240530101000"
},
"Children": [
{
"Type": "NodeText",
"Data": ""
},
{
"Type": "NodeTextMark",
"TextMarkType": "code",
"TextMarkTextContent": "queryBlocks"
},
{
"Type": "NodeText",
"Data": " と "
},
{
"Type": "NodeTextMark",
"TextMarkType": "code",
"TextMarkTextContent": "querySpans"
},
{
"Type": "NodeText",
"Data": " はパラメータの入力を容易にするために、SQL のプリペアドステートメントに似た可変長引数リストをサポートしています:"
}
]
},
{
"ID": "20240530101000-ao36z4x",
"Type": "NodeCodeBlock",
"IsFencedCodeBlock": true,
"Properties": {
"ID": "20240530101000-2pwl178",
"id": "20240530101000-ao36z4x",
"updated": "20240530101000"
},
"Children": [
{
"Type": "NodeCodeBlockFenceOpenMarker",
"Data": "```"
},
{
"Type": "NodeCodeBlockFenceInfoMarker"
},
{
"ID": "20240530101000-ry5jzum",
"Type": "NodeCodeBlockCode",
"Data": ".action{$today := now | date \"20060102150405\"}\n.action{$blocks :=queryBlocks \"SELECT * FROM blocks WHERE content LIKE '?' AND updated \u003e '?' LIMIT ?\" \"%foo%\" $today \"3\"}\n",
"Properties": {
"ID": "20240530101000-i3tqwi3",
"id": "20240530101000-ry5jzum"
}
},
{
"Type": "NodeCodeBlockFenceCloseMarker",
"Data": "```"
}
]
},
{
"ID": "20240530101000-ud82uol",
"Type": "NodeHeading",

View file

@ -7,7 +7,7 @@
"id": "20240530101000-dro2zi9",
"title": "ドキュメントと見出しの変換",
"type": "doc",
"updated": "20241018110150"
"updated": "20241214173436"
},
"Children": [
{
@ -149,7 +149,7 @@
"Properties": {
"ID": "20240530101000-g3g26mu",
"id": "20240530101000-7z8z1w9",
"updated": "20240530101000"
"updated": "20241214173436"
},
"Children": [
{
@ -162,14 +162,13 @@
"ID": "20240530101000-281oxo0",
"Type": "NodeParagraph",
"Properties": {
"ID": "20240530101000-wtkhsae",
"id": "20240530101000-281oxo0",
"updated": "20240530101000"
"updated": "20241214173436"
},
"Children": [
{
"Type": "NodeText",
"Data": "ドキュメントツリーで見出しブロックに変換するドキュメントブロックを選択し、開かれているエディタの挿入したい位置にドラッグします。ここでは、以下の 2 つのパターンがあります:"
"Data": "ドキュメントツリーでタイトルブロックに変換するドキュメントを選択し、Altキーを押しながらエディタタブの挿入したい位置にドラッグします。ここでは、以下の 2 つのパターンがあります:"
}
]
},

View file

@ -1,6 +1,6 @@
{
"name": "SiYuan",
"version": "3.1.14",
"version": "3.1.15",
"description": "Refactor your thinking",
"homepage": "https://b3log.org/siyuan",
"main": "./electron/main.js",

8
app/pnpm-lock.yaml generated
View file

@ -1942,8 +1942,8 @@ packages:
ms@2.1.2:
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
nanoid@3.3.6:
resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==}
nanoid@3.3.8:
resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
@ -4891,7 +4891,7 @@ snapshots:
ms@2.1.2: {}
nanoid@3.3.6: {}
nanoid@3.3.8: {}
natural-compare@1.4.0: {}
@ -5110,7 +5110,7 @@ snapshots:
postcss@8.4.31:
dependencies:
nanoid: 3.3.6
nanoid: 3.3.8
picocolors: 1.0.0
source-map-js: 1.0.2

View file

@ -197,8 +197,11 @@ export const initAnno = (element: HTMLElement, pdf: any) => {
} else if (type === "remove") {
const urlPath = pdf.appConfig.file.replace(location.origin, "").substr(1);
const config = getConfig(pdf);
delete config[rectElement.getAttribute("data-node-id")];
rectElement.remove();
const id = rectElement.getAttribute("data-node-id");
delete config[id];
element.querySelectorAll(`[data-node-id="${id}"]`).forEach(item => {
item.remove();
});
fetchPost("/api/asset/setFileAnnotation", {
path: urlPath + ".sya",
data: JSON.stringify(config),
@ -393,7 +396,7 @@ const showToolbar = (element: HTMLElement, range: Range, target?: HTMLElement) =
};
const getTextNode = (element: HTMLElement, isFirst: boolean) => {
const spans = element.querySelectorAll(".markedContent span");
const spans = element.querySelectorAll('span[role="presentation"]');
let index = isFirst ? 0 : spans.length - 1;
while (spans[index]) {
if (spans[index].textContent) {

View file

@ -0,0 +1,92 @@
This Font Software is licensed under the SIL Open Font License,
Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font
creation efforts of academic and linguistic communities, and to
provide a free and open framework in which fonts may be shared and
improved in partnership with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply to
any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software
components as distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to,
deleting, or substituting -- in part or in whole -- any of the
components of the Original Version, by changing formats or by porting
the Font Software to a new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed,
modify, redistribute, and sell modified and unmodified copies of the
Font Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components, in
Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the
corresponding Copyright Holder. This restriction only applies to the
primary font name as presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created using
the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

View file

@ -35,6 +35,7 @@
@import "business/resize";
@import "business/av";
@import "business/emojis";
@import "component/svg";
/*
.status: 2
@ -93,25 +94,6 @@ html {
}
}
.svg {
fill: currentColor;
display: inline-block;
stroke-width: 0;
stroke: currentColor;
width: 14px;
height: 14px;
flex-shrink: 0;
&--mid {
width: 16px;
height: 16px;
}
&--small {
width: 12px;
height: 12px;
}
}
.toolbar {
background-color: var(--b3-toolbar-background);

View file

@ -314,8 +314,18 @@
&--select {
background-color: var(--b3-theme-primary-lightest);
box-shadow: 2px 2px 0 var(--b3-theme-primary-lighter) inset, -2px -2px 0 var(--b3-theme-primary-lighter) inset;
border-radius: var(--b3-border-radius);
&::after {
content: '';
position: absolute;
top: 2px;
left: 2px;
right: 2px;
bottom: 2px;
border-radius: var(--b3-border-radius-s);
box-shadow: 0 0 0 2px var(--b3-theme-primary-lighter);
pointer-events: none;
}
}
&--active {

View file

@ -21,8 +21,11 @@
margin: 0 8px 0 0;
}
&:hover:not(.color__square--list),
&:hover:not(.color__square--list):not(.color__square--current) {
box-shadow: 0 0 0 1px var(--b3-border-color) inset, 0 0 0 3px var(--b3-list-hover);
}
&--current {
box-shadow: 0 0 0 1px var(--b3-list-hover) inset, 0 0 0 4px var(--b3-theme-background);
box-shadow: 0 0 0 2px var(--b3-menu-background) inset, 0 0 0 3px var(--b3-list-hover);
}
}

View file

@ -34,7 +34,6 @@
color: var(--b3-protyle-inline-blockref-color);
opacity: .86;
transition: var(--b3-transition);
flex: 1;
&:hover {
cursor: pointer;

View file

@ -27,7 +27,7 @@
.emoji__dynamic {
&-item {
width: 74px;
width: 73px;
margin: 8px;
cursor: pointer;
transition: var(--b3-transition);
@ -46,12 +46,11 @@
}
&__item {
font-size: 24px;
line-height: .9em; // windows 需要这样设置
font-size: 19px;
font-family: var(--b3-font-family-emoji);
text-align: center;
height: 28px;
padding: 2px 4px;
height: 32px;
padding: 4px;
cursor: pointer;
display: inline-block;
transition: var(--b3-transition);
@ -60,7 +59,7 @@
margin: 1px;
overflow: hidden;
border-radius: var(--b3-border-radius);
min-width: 32px;
width: 32px;
img, svg {
height: 24px;
@ -103,6 +102,7 @@
background-color: transparent;
border: 0;
padding: 0;
font-family: var(--b3-font-family-emoji);
&:hover {
background-color: var(--b3-theme-surface-lighter);

View file

@ -243,6 +243,7 @@
}
&--readonly {
cursor: default;
padding-left: 13px;
padding-right: 8px;
max-width: none;

View file

@ -19,6 +19,15 @@
width: 16px;
}
&-list {
height: 28px;
width: 42px;
display: flex;
justify-content: center;
align-items: center;
position: absolute;
}
&-input {
padding-left: 35px !important;
}

View file

@ -125,7 +125,7 @@
}
text-align: center;
font-size: 16px;
font-size: 14px;
font-family: var(--b3-font-family-emoji);
margin-right: 4px;
line-height: 22px;

View file

@ -0,0 +1,24 @@
.svg {
fill: currentColor;
display: inline-block;
stroke-width: 0;
stroke: currentColor;
width: 14px;
height: 14px;
flex-shrink: 0;
&--mid {
width: 16px;
height: 16px;
}
&--small {
width: 12px;
height: 12px;
}
&--smaller {
width: 10px;
height: 10px;
}
}

View file

@ -3,6 +3,32 @@
src: url(../fonts/JetBrainsMono-2.304/JetBrainsMono-Regular.woff2) format('woff2');
}
@font-face {
font-family: "Number Glyphs Of JetBrainsMono-Regular";
src: url(../fonts/JetBrainsMono-2.304/JetBrainsMono-Regular.woff2) format('woff2');
/* 数字 0-9 */
unicode-range: U+30-39;
}
@font-face {
font-family: "SiYuan Emojis";
src: url(../fonts/Noto-COLRv1-2.047/Noto-COLRv1.woff2) format("woff2");
// 表情中包含的 emoji 对应的 Unicode
unicode-range: U+23, U+2A, U+30-39, U+A9, U+AE, U+200D, U+203C, U+2049, U+2122, U+2139, U+2194-2199, U+21A9-21AA, U+231A-231B, U+2328, U+23CF, U+23E9-23F3, U+23F8-23FA, U+24C2, U+25AA-25AB, U+25B6, U+25C0, U+25FB-25FE, U+2600-2604, U+260E, U+2611, U+2614-2615, U+2618, U+261D, U+2620, U+2622-2623, U+2626, U+262A, U+262E-262F, U+2638-263A, U+2640, U+2642, U+2648-2653, U+265F-2660, U+2663, U+2665-2666, U+2668, U+267B, U+267E-267F, U+2692-2697, U+2699, U+269B-269C, U+26A0-26A1, U+26A7, U+26AA-26AB, U+26B0-26B1, U+26BD-26BE, U+26C4-26C5, U+26C8, U+26CE-26CF, U+26D1, U+26D3-26D4, U+26E9-26EA, U+26F0-26F5, U+26F7-26FA, U+26FD, U+2702, U+2705, U+2708-270D, U+270F, U+2712, U+2714, U+2716, U+271D, U+2721, U+2728, U+2733-2734, U+2744, U+2747, U+274C, U+274E, U+2753-2755, U+2757, U+2763-2764, U+2795-2797, U+27A1, U+27B0, U+27BF, U+2934-2935, U+2B05-2B07, U+2B1B-2B1C, U+2B50, U+2B55, U+3030, U+303D, U+3297, U+3299, U+E50A, U+1F004, U+1F0CF, U+1F170-1F171, U+1F17E-1F17F, U+1F18E, U+1F191-1F19A, U+1F1E6-1F1FF, U+1F201-1F202, U+1F21A, U+1F22F, U+1F232-1F23A, U+1F250-1F251, U+1F300-1F321, U+1F324-1F393, U+1F396-1F397, U+1F399-1F39B, U+1F39E-1F3F0, U+1F3F3-1F3F5, U+1F3F7-1F4FD, U+1F4FF-1F53D, U+1F549-1F54E, U+1F550-1F567, U+1F56F-1F570, U+1F573-1F57A, U+1F587, U+1F58A-1F58D, U+1F590, U+1F595-1F596, U+1F5A4-1F5A5, U+1F5A8, U+1F5B1-1F5B2, U+1F5BC, U+1F5C2-1F5C4, U+1F5D1-1F5D3, U+1F5DC-1F5DE, U+1F5E1, U+1F5E3, U+1F5E8, U+1F5EF, U+1F5F3, U+1F5FA-1F64F, U+1F680-1F6C5, U+1F6CB-1F6D2, U+1F6D5-1F6D7, U+1F6DC-1F6E5, U+1F6E9, U+1F6EB-1F6EC, U+1F6F0, U+1F6F3-1F6FC, U+1F7E0-1F7EB, U+1F7F0, U+1F90C-1F93A, U+1F93C-1F945, U+1F947-1F9FF, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8;
}
@font-face {
font-family: "Emojis";
src: local("Segoe UI Emoji"),
local("Segoe UI Symbol"),
local("Apple Color Emoji"),
local("Twemoji Mozilla"),
local("Noto Color Emoji"),
local("Android Emoji"),
local("EmojiSymbols"),
local("SiYuan Emojis");
}
.b3-typography,
.protyle-wysiwyg {
font-variant-ligatures: no-common-ligatures;

View file

@ -28,6 +28,7 @@
@import "business/av";
@import "business/search";
@import "business/emojis";
@import "component/svg";
.block__popover {
width: 80vw;
@ -138,6 +139,15 @@
&:not(.toolbar__icon-deactivate):hover {
background-color: var(--b3-list-hover);
}
&--history {
height: 36px;
width: 36px;
display: flex;
justify-content: center;
align-items: center;
padding: 0;
}
}
&__title {
@ -152,10 +162,6 @@
color: var(--b3-theme-on-background);
}
&__search {
margin: 8px;
}
&__text {
@include text-clamp(1);
flex: 1;
@ -395,6 +401,7 @@
stroke: currentColor;
width: 14px;
height: 14px;
flex-shrink: 0;
&--mid {
width: 16px;
@ -405,6 +412,11 @@
width: 12px;
height: 12px;
}
&--smaller {
width: 10px;
height: 10px;
}
}
#empty {

View file

@ -52,6 +52,7 @@
min-height: 0 !important;
min-width: 0 !important;
width: 100%;
pointer-events: none;
}
}

View file

@ -121,13 +121,11 @@ export class BlockPanel {
openNewWindowById(this.nodeIds[0]);
/// #endif
} else if (type === "stickTab") {
/// #if !BROWSER
openFileById({
app: options.app,
id: this.nodeIds[0],
action: this.editors[0].protyle.block.rootID !== this.nodeIds[0] ? [Constants.CB_GET_ALL, Constants.CB_GET_FOCUS] : [Constants.CB_GET_CONTEXT],
});
/// #endif
}
event.preventDefault();
event.stopPropagation();
@ -233,14 +231,14 @@ export class BlockPanel {
return;
}
let openHTML = "";
/// #if !BROWSER
if (this.nodeIds.length === 1) {
openHTML = `<span data-type="stickTab" class="block__icon block__icon--show b3-tooltips b3-tooltips__sw" aria-label="${window.siyuan.languages.openBy}"><svg><use xlink:href="#iconOpen"></use></svg></span>
<span class="fn__space"></span>
<span data-type="open" class="block__icon block__icon--show b3-tooltips b3-tooltips__sw" aria-label="${window.siyuan.languages.openByNewWindow}"><svg><use xlink:href="#iconOpenWindow"></use></svg></span>
<span class="fn__space"></span>`;
/// #if !BROWSER
openHTML += `<span data-type="open" class="block__icon block__icon--show b3-tooltips b3-tooltips__sw" aria-label="${window.siyuan.languages.openByNewWindow}"><svg><use xlink:href="#iconOpenWindow"></use></svg></span>
<span class="fn__space"></span>`;
/// #endif
}
/// #endif
let html = `<div class="block__icons block__icons--menu">
<span class="fn__space fn__flex-1 resize__move"></span>${openHTML}
<span data-type="pin" class="block__icon block__icon--show b3-tooltips b3-tooltips__sw" aria-label="${window.siyuan.languages.pin}"><svg><use xlink:href="#iconPin"></use></svg></span>

View file

@ -54,7 +54,7 @@ export const initBlockPopover = (app: App) => {
aElement.style.overflow = "";
}
}
} else if (aElement.parentElement.parentElement.classList.contains("av__views")) {
} else if (aElement.parentElement.parentElement.classList.contains("av__views") && aElement.parentElement.classList.contains("layout-tab-bar")) {
const textElement = aElement.querySelector(".item__text");
const desc = aElement.getAttribute("data-desc");
if (textElement.scrollWidth > textElement.clientWidth + 2 || desc) {

View file

@ -10,13 +10,14 @@ import {App} from "../../index";
import {Dialog} from "../../dialog";
import {getAllModels} from "../../layout/getAll";
import {hasClosestByClassName} from "../../protyle/util/hasClosest";
import {getArticle, inputEvent, replace, toggleReplaceHistory, toggleSearchHistory} from "../../search/util";
import {getArticle, inputEvent, replace} from "../../search/util";
import {showFileInFolder} from "../../util/pathName";
import {assetInputEvent, renderPreview, toggleAssetHistory} from "../../search/assets";
import {assetInputEvent, renderPreview} from "../../search/assets";
import {initSearchMenu} from "../../menus/search";
import {writeText} from "../../protyle/util/compatibility";
import {checkFold} from "../../util/noRelyPCFunction";
import {getUnRefList} from "../../search/unRef";
import {toggleAssetHistory, toggleReplaceHistory, toggleSearchHistory} from "../../search/toggleHistory";
export const searchKeydown = (app: App, event: KeyboardEvent) => {
if (getSelection().rangeCount === 0) {
@ -72,7 +73,7 @@ export const searchKeydown = (app: App, event: KeyboardEvent) => {
toggleAssetHistory(assetsElement);
} else if (searchType === "doc") {
if (targetId === "replaceInput") {
toggleReplaceHistory(element);
toggleReplaceHistory(element.querySelector("#replaceInput"));
} else {
toggleSearchHistory(element, config, edit);
}

View file

@ -28,13 +28,13 @@ export const image = {
<div class="fn__flex-1">
<div class="config-assets" data-type="remove" data-init="true">
<div class="fn__hr--b"></div>
<label class="fn__flex">
<div class="fn__flex">
<div class="fn__space"></div>
<button id="removeAll" class="b3-button b3-button--outline fn__flex-center fn__size200">
<svg class="svg"><use xlink:href="#iconTrashcan"></use></svg>
${window.siyuan.languages.delete}
</button>
</label>
</div>
<div class="fn__hr"></div>
<ul class="b3-list b3-list--background config-assets__list">
<li class="fn__loading"><img src="/stage/loading-pure.svg"></li>

View file

@ -392,6 +392,16 @@ export const repos = {
<option value="3" ${window.siyuan.config.sync.mode === 3 ? "selected" : ""}>${window.siyuan.languages.syncMode3}</option>
</select>
</div>
<label class="fn__flex b3-label${(window.siyuan.config.sync.mode !== 1 || window.siyuan.config.system.container === "docker" || window.siyuan.config.sync.provider !== 0) ? " fn__none" : ""}">
<div class="fn__flex-1">
${window.siyuan.languages.syncInterval}
<div class="b3-label__text">${window.siyuan.languages.syncIntervalTip}</div>
</div>
<span class="fn__space"></span>
<input type="number" min="30" max="43200" id="syncInterval" class="b3-text-field fn__flex-center" value="${window.siyuan.config.sync.interval}" >
<span class="fn__space"></span>
<span class="fn__flex-center ft__on-surface">${window.siyuan.languages.second}</span>
</label>
<label class="fn__flex b3-label${(window.siyuan.config.sync.mode !== 1 || window.siyuan.config.system.container === "docker" || window.siyuan.config.sync.provider !== 0) ? " fn__none" : ""}">
<div class="fn__flex-1">
${window.siyuan.languages.syncPerception}
@ -434,6 +444,21 @@ export const repos = {
processSync();
});
});
const syncIntervalElement = repos.element.querySelector("#syncInterval") as HTMLInputElement;
syncIntervalElement.addEventListener("change", () => {
let interval = parseInt(syncIntervalElement.value);
if (30 > interval) {
interval = 30;
}
if (43200 < interval) {
interval = 43200;
}
syncIntervalElement.value = interval.toString();
fetchPost("/api/sync/setSyncInterval", {interval: interval}, () => {
window.siyuan.config.sync.interval = interval;
processSync();
});
});
const syncPerceptionElement = repos.element.querySelector("#syncPerception") as HTMLInputElement;
syncPerceptionElement.addEventListener("change", () => {
fetchPost("/api/sync/setSyncPerception", {enabled: syncPerceptionElement.checked}, () => {
@ -452,8 +477,10 @@ export const repos = {
fetchPost("/api/sync/setSyncMode", {mode: parseInt(syncModeElement.value, 10)}, () => {
if (syncModeElement.value === "1" && window.siyuan.config.sync.provider === 0 && window.siyuan.config.system.container !== "docker") {
syncPerceptionElement.parentElement.classList.remove("fn__none");
syncIntervalElement.parentElement.classList.remove("fn__none");
} else {
syncPerceptionElement.parentElement.classList.add("fn__none");
syncIntervalElement.parentElement.classList.add("fn__none");
}
window.siyuan.config.sync.mode = parseInt(syncModeElement.value, 10);
});
@ -475,8 +502,10 @@ export const repos = {
syncConfigElement.classList.add("fn__none");
if (window.siyuan.config.sync.mode !== 1 || window.siyuan.config.system.container === "docker" || window.siyuan.config.sync.provider !== 0) {
syncPerceptionElement.parentElement.classList.add("fn__none");
syncIntervalElement.parentElement.classList.add("fn__none");
} else {
syncPerceptionElement.parentElement.classList.remove("fn__none");
syncIntervalElement.parentElement.classList.remove("fn__none");
}
});
});

View file

@ -124,6 +124,7 @@ export abstract class Constants {
public static readonly LOCAL_PLUGIN_DOCKS = "local-plugin-docks";
public static readonly LOCAL_IMAGES = "local-images";
public static readonly LOCAL_EMOJIS = "local-emojis";
public static readonly LOCAL_MOVE_PATH = "local-move-path";
// dialog
public static readonly DIALOG_CONFIRM = "dialog-confirm";

View file

@ -249,7 +249,7 @@ export const openEmojiPanel = (id: string, type: "doc" | "notebook" | "av", posi
disableAnimation: true,
transparent: true,
hideCloseIcon: true,
width: isMobile() ? "80vw" : "372px",
width: isMobile() ? "80vw" : "368px",
height: "50vh",
content: `<div class="emojis">
<div class="emojis__tabheader">

View file

@ -37,7 +37,7 @@ import {Asset} from "../asset";
import {newFile} from "../util/newFile";
import {MenuItem} from "../menus/Menu";
import {escapeHtml} from "../util/escape";
import {isWindow} from "../util/functions";
import {getFrontend, isWindow} from "../util/functions";
import {hideAllElements} from "../protyle/ui/hideElements";
import {focusByOffset, getSelectionOffset} from "../protyle/util/selection";
import {Custom} from "./dock/Custom";
@ -101,11 +101,29 @@ export class Wnd {
window.siyuan.menus.menu.remove();
event.stopPropagation();
event.preventDefault();
const frontend = getFrontend();
if ((["desktop", "desktop-window"].includes(frontend) && window.siyuan.config.system.os === "linux") ||
(frontend === "browser-desktop" && navigator.userAgent.indexOf("Linux") !== -1)) {
const activeElement = document.activeElement;
window.addEventListener("paste", this.#preventPast, {
capture: true,
once: true
});
// TODO 保持原有焦点https://github.com/siyuan-note/siyuan/pull/13395/files#r1877004077
if (activeElement instanceof HTMLElement) {
activeElement.focus();
}
// 如果在短时间内没有 paste 事件发生,移除监听
setTimeout(() => {
window.removeEventListener("paste", this.#preventPast, {
capture: true
});
}, Constants.TIMEOUT_INPUT);
}
break;
}
target = target.parentElement;
}
});
this.headersElement.addEventListener("mousewheel", (event: WheelEvent) => {
this.headersElement.scrollLeft = this.headersElement.scrollLeft + event.deltaY;
@ -590,6 +608,11 @@ export class Wnd {
}
}
#preventPast(event: ClipboardEvent) {
event.preventDefault();
event.stopPropagation();
}
private renderTabList(target: HTMLElement) {
if (!window.siyuan.menus.menu.element.classList.contains("fn__none") &&
window.siyuan.menus.menu.element.getAttribute("data-name") === "tabList") {

View file

@ -467,7 +467,7 @@ export class Backlink extends Model {
}
});
editor.protyle.notebookId = liElement.getAttribute("data-notebook-id");
searchMarkRender(editor.protyle, keyword.split(" "));
searchMarkRender(editor.protyle, response.data.keywords);
this.editors.push(editor);
});
}

View file

@ -390,6 +390,10 @@ export class Outline extends Model {
id: this.blockId,
preview: this.isPreview
}, response => {
// 文档切换后不再更新原有推送 https://github.com/siyuan-note/siyuan/issues/13409
if (data.data.rootID !== this.blockId) {
return;
}
this.update(response);
// https://github.com/siyuan-note/siyuan/issues/8372
if (getSelection().rangeCount > 0) {

View file

@ -1,13 +1,19 @@
export const openModel = (obj: {
html: string,
icon: string,
icon?: string,
title: string,
bindEvent: (element: HTMLElement) => void
}) => {
const modelElement = document.getElementById("model");
modelElement.style.transform = "translateY(0px)";
modelElement.style.zIndex = (++window.siyuan.zIndex).toString();
modelElement.querySelector(".toolbar__icon use").setAttribute("xlink:href", "#" + obj.icon);
const iconElement = modelElement.querySelector(".toolbar__icon");
if(obj.icon) {
iconElement.classList.remove("fn__none");
iconElement.querySelector("use").setAttribute("xlink:href", "#" + obj.icon);
} else {
iconElement.classList.add("fn__none");
}
modelElement.querySelector(".toolbar__text").innerHTML = obj.title;
const modelMainElement = modelElement.querySelector("#modelMain") as HTMLElement;
modelMainElement.innerHTML = obj.html;

View file

@ -26,6 +26,13 @@ import {
import {addClearButton} from "../../util/addClearButton";
import {checkFold} from "../../util/noRelyPCFunction";
import {getDefaultType} from "../../search/getDefault";
import {
saveAssetKeyList,
saveKeyList,
toggleAssetHistory,
toggleReplaceHistory,
toggleSearchHistory
} from "../../search/toggleHistory";
const replace = (element: Element, config: Config.IUILayoutTabSearchConfig, isAll: boolean) => {
if (config.method === 1 || config.method === 2) {
@ -39,6 +46,7 @@ const replace = (element: Element, config: Config.IUILayoutTabSearchConfig, isAl
if (!loadElement.classList.contains("fn__none")) {
return;
}
saveKeyList("replaceKeys", replaceInputElement.value);
let currentLiElement: HTMLElement = searchListElement.querySelector(".b3-list-item--focus");
if (!currentLiElement) {
return;
@ -287,6 +295,7 @@ const initSearchEvent = (app: App, element: Element, config: Config.IUILayoutTab
window.siyuan.storage[Constants.LOCAL_SEARCHDATA] = Object.assign({}, config);
setStorageVal(Constants.LOCAL_SEARCHDATA, window.siyuan.storage[Constants.LOCAL_SEARCHDATA]);
}
saveKeyList("keys", searchInputElement.value);
});
addClearButton({
inputElement: searchInputElement,
@ -313,7 +322,17 @@ const initSearchEvent = (app: App, element: Element, config: Config.IUILayoutTab
let target = event.target as HTMLElement;
while (target && !target.isSameNode(element)) {
const type = target.getAttribute("data-type");
if (type === "previous") {
if (type === "replaceHistory") {
toggleReplaceHistory(target.nextElementSibling as HTMLInputElement);
event.stopPropagation();
event.preventDefault();
break;
} else if (type === "assetHistory") {
toggleAssetHistory(assetsElement);
event.stopPropagation();
event.preventDefault();
break;
} else if (type === "previous") {
if (!target.getAttribute("disabled")) {
config.page--;
updateSearchResult(config, element);
@ -663,13 +682,19 @@ export const popSearch = (app: App, searchConfig?: any) => {
openModel({
title: `<div class="fn__flex">
<span data-menu="true" class="toolbar__icon toolbar__icon--history" data-type="history">
<svg class="svg--mid"><use xlink:href="#iconSearch"></use></svg>
<svg class="svg--smaller"><use xlink:href="#iconDown"></use></svg>
</span>
<input id="toolbarSearch" placeholder="${window.siyuan.languages.showRecentUpdatedBlocks}" class="toolbar__title fn__block">
<svg id="toolbarSearchNew" class="toolbar__icon"><use xlink:href="#iconFile"></use></svg>
</div>`,
icon: "iconSearch",
html: `<div class="fn__flex-column" style="height: 100%">
<div class="toolbar toolbar--border${config.hasReplace ? "" : " fn__none"}">
<svg class="toolbar__icon"><use xlink:href="#iconReplace"></use></svg>
<span data-menu="true" class="toolbar__icon toolbar__icon--history" data-type="replaceHistory">
<svg class="svg--mid"><use xlink:href="#iconReplace"></use></svg>
<svg class="svg--smaller"><use xlink:href="#iconDown"></use></svg>
</span>
<input id="toolbarReplace" class="toolbar__title">
<svg class="fn__rotate fn__none toolbar__icon"><use xlink:href="#iconRefresh"></use></svg>
<div class="fn__space"></div>
@ -707,7 +732,10 @@ export const popSearch = (app: App, searchConfig?: any) => {
</div>
<div class="fn__none fn__flex-column" style="position: fixed;top: 0;width: 100%;background: var(--b3-theme-surface);height: 100%;" id="searchAssetsPanel">
<div class="toolbar toolbar--border">
<svg class="toolbar__icon"><use xlink:href="#iconSearch"></use></svg>
<span data-menu="true" class="toolbar__icon toolbar__icon--history" data-type="assetHistory">
<svg class="svg--mid"><use xlink:href="#iconSearch"></use></svg>
<svg class="svg--smaller"><use xlink:href="#iconDown"></use></svg>
</span>
<input id="searchAssetInput" placeholder="${window.siyuan.languages.keyword}" class="toolbar__title fn__block">
</div>
<div class="toolbar">
@ -750,6 +778,10 @@ export const popSearch = (app: App, searchConfig?: any) => {
document.querySelector("#toolbarSearchNew").addEventListener("click", () => {
newFileByName(app, (document.querySelector("#toolbarSearch") as HTMLInputElement).value);
});
const historyElement = document.querySelector('.toolbar [data-type="history"]');
historyElement.addEventListener("click", () => {
toggleSearchHistory(document.querySelector("#model"), config, undefined);
});
initSearchEvent(app, element.firstElementChild, config);
updateSearchResult(config, element);
}
@ -778,6 +810,9 @@ const goAsset = () => {
}
assetInputEvent(assetsElement, localSearch);
});
inputElement.addEventListener("blur", () => {
saveAssetKeyList(inputElement);
});
assetInputEvent(assetsElement, localSearch);
addClearButton({
inputElement,

View file

@ -18,6 +18,9 @@ const forwardStack: IBackStack[] = [];
const focusStack = (backStack: IBackStack) => {
const protyle = getCurrentEditor().protyle;
// 前进后快速后退会导致滚动错位 https://ld246.com/article/1734018624070
protyle.observerLoad.disconnect();
window.siyuan.storage[Constants.LOCAL_DOCINFO] = {
id: backStack.id,
};
@ -95,6 +98,10 @@ const focusStack = (backStack: IBackStack) => {
setReadonlyByConfig(protyle, true);
}
protyle.contentElement.scrollTop = backStack.scrollTop;
// 等待 av 等加载 https://ld246.com/article/1734018624070
setTimeout(() => {
protyle.contentElement.scrollTop = backStack.scrollTop;
}, Constants.TIMEOUT_LOAD);
});
};

View file

@ -318,6 +318,9 @@ const renderPDF = async (id: string) => {
item.style.width = Math.min(item.clientWidth, width) + "px";
item.removeAttribute('data-render');
})
previewElement.querySelectorAll('[data-type="NodeCodeBlock"][data-subtype="mermaid"] svg').forEach((item) => {
item.style.maxHeight = width * 1.414 + "px";
})
Protyle.mathRender(previewElement, "${servePath}/stage/protyle", true);
previewElement.querySelectorAll("table").forEach(item => {
if (item.clientWidth > item.parentElement.clientWidth) {

View file

@ -443,8 +443,8 @@ export class Background {
if (tags) {
let html = "";
const colors = ["secondary", "primary", "info", "success", "warning", "error", "pink"];
tags.split(",").forEach((item, index) => {
if (!item) {
new Set(tags.split(",")).forEach((item, index) => {
if (!item.replace(/ /g, "")) {
return;
}
html += `<div class="b3-chip b3-chip--middle b3-chip--pointer b3-chip--${colors[index % 7]}" data-type="open-search">${escapeHtml(item)}<svg class="b3-chip__close" data-type="remove-tag"><use xlink:href="#iconCloseRound"></use></svg></div>`;
@ -522,8 +522,12 @@ export class Background {
k: "",
}, (response) => {
let html = "";
const currentTags = this.getTags();
response.data.tags.forEach((item: string, index: number) => {
html += `<div class="b3-list-item b3-list-item--narrow${index === 0 ? " b3-list-item--focus" : ""}">${item}</div>`;
html += `<div class="b3-list-item b3-list-item--narrow${index === 0 ? " b3-list-item--focus" : ""}">
<div class="fn__flex-1">${item}</div>
${currentTags.includes(Lute.UnEscapeHTMLStr(item))?'<svg class="b3-menu__checked"><use xlink:href="#iconSelect"></use></svg>':""}
</div>`;
});
listElement.innerHTML = html;
});
@ -537,12 +541,12 @@ export class Background {
if (event.key === "Enter") {
const currentElement = listElement.querySelector(".b3-list-item--focus");
if (currentElement) {
this.addTags(currentElement.textContent, protyle);
this.addTags(currentElement.textContent.trim(), protyle);
} else {
this.addTags(inputElement.value, protyle);
this.addTags(inputElement.value.trim(), protyle);
}
inputElement.value = "";
inputElement.dispatchEvent(new CustomEvent("input"));
inputElement.dispatchEvent(new CustomEvent("input"));
} else if (event.key === "Escape") {
window.siyuan.menus.menu.remove();
}
@ -554,8 +558,12 @@ export class Background {
}, (response) => {
let searchHTML = "";
let hasKey = false;
const currentTags = this.getTags();
response.data.tags.forEach((item: string) => {
searchHTML += `<div class="b3-list-item b3-list-item--narrow">${item}</div>`;
searchHTML += `<div class="b3-list-item b3-list-item--narrow">
<div class="fn__flex-1">${item}</div>
${currentTags.includes(Lute.UnEscapeHTMLStr(item.replace(/<mark>/g,"").replace(/<\/mark>/g,"")))?'<svg class="b3-menu__checked"><use xlink:href="#iconSelect"></use></svg>':""}
</div>`;
if (item === `<mark>${response.data.k}</mark>`) {
hasKey = true;
}
@ -573,7 +581,8 @@ export class Background {
if (!listItemElement) {
return;
}
this.addTags(listItemElement.textContent, protyle);
this.addTags(listItemElement.textContent.trim(), protyle);
inputElement.dispatchEvent(new CustomEvent("input"));
});
}
});

View file

@ -34,7 +34,7 @@ export const addFilesToDatabase = (fileLiElements: Element[]) => {
};
export const addEditorToDatabase = (protyle: IProtyle, range: Range, type?: string) => {
if (protyle.title?.editElement?.contains(range.startContainer) || type === "title") {
if ((range && protyle.title?.editElement?.contains(range.startContainer)) || type === "title") {
openSearchAV("", protyle.breadcrumb.element, (listItemElement) => {
const avID = listItemElement.dataset.avId;
transaction(protyle, [{

View file

@ -184,6 +184,7 @@ export const renderAVAttribute = (element: HTMLElement, id: string, protyle: IPr
<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>
<div class="fn__flex-1"></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>`;

View file

@ -307,7 +307,6 @@ export const cellScrollIntoView = (blockElement: HTMLElement, cellElement: Eleme
if (contentElement && cellElement.getAttribute("data-dtype") !== "checkbox") {
const keyboardToolbarElement = document.getElementById("keyboardToolbar");
const keyboardH = parseInt(keyboardToolbarElement.getAttribute("data-keyboardheight")) || (window.outerHeight / 2 - 42);
console.log(keyboardH, window.innerHeight, cellRect.bottom);
if (cellRect.bottom > window.innerHeight - keyboardH - 42) {
contentElement.scrollTop += cellRect.bottom - window.innerHeight + 42 + keyboardH;
} else if (cellRect.top < 110) {

View file

@ -393,7 +393,7 @@ export const bindEditEvent = (options: {
return;
}
colData.options.push({
color: (colData.options.length + 1).toString(),
color: ((colData.options.length || 0) % 14 + 1).toString(),
name: addOptionElement.value
});
transaction(options.protyle, [{

View file

@ -349,7 +349,11 @@ ${cell.color ? `color:${cell.color};` : ""}">${renderCell(cell.value, rowIndex)}
if (isSearching) {
searchInputElement.focus();
}
searchInputElement.addEventListener("compositionstart", (event: KeyboardEvent) => {
event.stopPropagation();
});
searchInputElement.addEventListener("input", (event: KeyboardEvent) => {
event.stopPropagation();
if (event.isComposing) {
return;
}

View file

@ -46,7 +46,7 @@ const filterSelectHTML = (key: string, options: {
});
}
if (!hasMatch && key) {
const colorIndex = (options?.length || 0) % 13 + 1;
const colorIndex = (options?.length || 0) % 14 + 1;
html = `<button data-type="addColOptionOrCell" class="b3-menu__item b3-menu__item--current" data-name="${key}" data-color="${colorIndex}">
<svg class="b3-menu__icon"><use xlink:href="#iconAdd"></use></svg>
<div class="fn__flex-1">
@ -318,87 +318,91 @@ export const setColOption = (protyle: IProtyle, data: IAV, target: HTMLElement,
}
});
menu.addSeparator();
Array.from(Array(13).keys()).forEach(index => {
menu.addItem({
checked: parseInt(color) === index + 1,
iconHTML: "",
label: `<span class="color__square color__square--list" style="margin: 2px 0;color: var(--b3-font-color${index + 1});background-color: var(--b3-font-background${index + 1});">A</span>`,
click(element) {
if (element.lastElementChild.classList.contains("b3-menu__checked")) {
return;
}
element.parentElement.querySelector(".b3-menu__checked")?.remove();
element.insertAdjacentHTML("beforeend", '<svg class="b3-menu__checked"><use xlink:href="#iconSelect"></use></svg></span>');
transaction(protyle, [{
action: "updateAttrViewColOption",
id: colId,
avID: data.id,
data: {
oldName: name,
newName: inputElement.value,
oldColor: color,
newColor: (index + 1).toString(),
newDesc: descElement.value
},
}], [{
action: "updateAttrViewColOption",
id: colId,
avID: data.id,
data: {
oldName: inputElement.value,
newName: name,
oldColor: (index + 1).toString(),
newColor: color,
newDesc: descElement.value
},
}]);
let html = "<div class=\"fn__flex fn__flex-wrap\" style=\"width: 238px\">";
Array.from(Array(14).keys()).forEach(index => {
html += `<button data-color="${index + 1}" class="color__square${parseInt(color) === index + 1 ? " color__square--current" : ""}" style="color: var(--b3-font-color${index + 1});background-color: var(--b3-font-background${index + 1});">A</button>`;
});
menu.addItem({
type: "empty",
iconHTML: "",
label: html + "</div>",
bind(element) {
element.addEventListener("click", (event) => {
const target = event.target as HTMLElement;
if (target.classList.contains("color__square") && !target.classList.contains("color__square--current")) {
element.querySelector(".color__square--current")?.classList.remove("color__square--current");
target.classList.add("color__square--current");
const newColor = target.getAttribute("data-color");
transaction(protyle, [{
action: "updateAttrViewColOption",
id: colId,
avID: data.id,
data: {
oldName: name,
newName: inputElement.value,
oldColor: color,
newColor,
newDesc: descElement.value
},
}], [{
action: "updateAttrViewColOption",
id: colId,
avID: data.id,
data: {
oldName: inputElement.value,
newName: name,
oldColor: newColor,
newColor: color,
newDesc: descElement.value
},
}]);
data.view.columns.find(column => {
if (column.id === colId) {
column.options.find((item) => {
if (item.name === name) {
item.name = inputElement.value;
item.color = (index + 1).toString();
return true;
}
});
return true;
}
});
const oldScroll = menuElement.querySelector(".b3-menu__items").scrollTop;
if (!cellElements) {
menuElement.innerHTML = getEditHTML({protyle, data, colId, isCustomAttr});
bindEditEvent({protyle, data, menuElement, isCustomAttr, blockID});
} else {
cellElements.forEach((cellElement: HTMLElement, cellIndex) => {
const rowElement = hasClosestByClassName(cellElement, "av__row");
if (rowElement) {
cellElement = cellElements[cellIndex] = (blockElement.querySelector(`.av__row[data-id="${rowElement.dataset.id}"] .av__cell[data-col-id="${cellElement.dataset.colId}"]`) ||
blockElement.querySelector(`.fn__flex-1[data-col-id="${cellElement.dataset.colId}"]`)) as HTMLElement;
}
cellValues[cellIndex].mSelect.find((item) => {
if (item.content === name) {
item.content = inputElement.value;
item.color = (index + 1).toString();
return true;
}
});
if (cellElement.classList.contains("custom-attr__avvalue")) {
cellElement.innerHTML = genAVValueHTML(cellValues[cellIndex]);
} else {
updateAttrViewCellAnimation(cellElement, cellValues[cellIndex]);
data.view.columns.find(column => {
if (column.id === colId) {
column.options.find((item) => {
if (item.name === name) {
item.name = inputElement.value;
item.color = newColor;
return true;
}
});
return true;
}
});
menuElement.innerHTML = getSelectHTML(data.view, cellElements);
bindSelectEvent(protyle, data, menuElement, cellElements, blockElement);
const oldScroll = menuElement.querySelector(".b3-menu__items").scrollTop;
if (!cellElements) {
menuElement.innerHTML = getEditHTML({protyle, data, colId, isCustomAttr});
bindEditEvent({protyle, data, menuElement, isCustomAttr, blockID});
} else {
cellElements.forEach((cellElement: HTMLElement, cellIndex) => {
const rowElement = hasClosestByClassName(cellElement, "av__row");
if (rowElement) {
cellElement = cellElements[cellIndex] = (blockElement.querySelector(`.av__row[data-id="${rowElement.dataset.id}"] .av__cell[data-col-id="${cellElement.dataset.colId}"]`) ||
blockElement.querySelector(`.fn__flex-1[data-col-id="${cellElement.dataset.colId}"]`)) as HTMLElement;
}
cellValues[cellIndex].mSelect.find((item) => {
if (item.content === name) {
item.content = inputElement.value;
item.color = newColor;
return true;
}
});
if (cellElement.classList.contains("custom-attr__avvalue")) {
cellElement.innerHTML = genAVValueHTML(cellValues[cellIndex]);
} else {
updateAttrViewCellAnimation(cellElement, cellValues[cellIndex]);
}
});
menuElement.innerHTML = getSelectHTML(data.view, cellElements);
bindSelectEvent(protyle, data, menuElement, cellElements, blockElement);
}
menuElement.querySelector(".b3-menu__items").scrollTop = oldScroll;
name = inputElement.value;
desc = descElement.value;
color = newColor;
}
menuElement.querySelector(".b3-menu__items").scrollTop = oldScroll;
name = inputElement.value;
desc = descElement.value;
color = (index + 1).toString();
return true;
}
});
});
}
});
const rect = target.getBoundingClientRect();
menu.open({
@ -630,7 +634,7 @@ export const mergeAddOption = (column: IAVColumn, cellValue: IAVCellValue, avID:
}
});
if (!needAdd) {
const newColor = ((column.options?.length || 0) % 13 + 1).toString();
const newColor = ((column.options?.length || 0) % 14 + 1).toString();
column.options.push({
name: item.content,
color: newColor

View file

@ -61,7 +61,9 @@ export const scrollEvent = (protyle: IProtyle, element: HTMLElement) => {
}
if (protyle.wysiwyg.element.getAttribute("data-top") || protyle.block.showAll ||
(protyle.scroll && protyle.scroll.element.classList.contains("fn__none")) || !protyle.scroll ||
protyle.scroll.lastScrollTop === element.scrollTop || protyle.scroll.lastScrollTop === -1) {
protyle.scroll.lastScrollTop === element.scrollTop || protyle.scroll.lastScrollTop === -1 ||
// 移动端跳转的时候会设置 wysiwyg.element.innerHTML = "";
!protyle.wysiwyg.element.firstElementChild) {
return;
}
if (protyle.scroll.lastScrollTop - element.scrollTop > 0) {

View file

@ -9,6 +9,7 @@ import {genAssetHTML} from "../../asset/renderAssets";
import {hasClosestBlock} from "../util/hasClosest";
import {getContenteditableElement} from "../wysiwyg/getBlock";
import {getTypeByCellElement, updateCellsValue} from "../render/av/cell";
import {scrollCenter} from "../../util/highlightById";
export class Upload {
public element: HTMLElement;
@ -118,28 +119,30 @@ const genUploadedLabel = (responseText: string, protyle: IProtyle) => {
}
}
}
let succFileText = "";
let successFileText = "";
const keys = Object.keys(response.data.succMap);
const avAssets: IAVCellAssetValue[] = [];
let hasImage = false;
keys.forEach((key, index) => {
const path = response.data.succMap[key];
const type = pathPosix().extname(key).toLowerCase();
const filename = protyle.options.upload.filename(key);
const name = filename.substring(0, filename.length - type.length);
hasImage = Constants.SIYUAN_ASSETS_IMAGE.includes(type);
avAssets.push({
type: Constants.SIYUAN_ASSETS_IMAGE.includes(type) ? "image" : "file",
content: path,
name: name
});
succFileText += genAssetHTML(type, path, name, filename);
successFileText += genAssetHTML(type, path, name, filename);
if (!Constants.SIYUAN_ASSETS_AUDIO.includes(type) && !Constants.SIYUAN_ASSETS_VIDEO.includes(type) &&
keys.length - 1 !== index) {
if (nodeElement && nodeElement.classList.contains("table")) {
succFileText += "<br>";
successFileText += "<br>";
} else if (insertBlock) {
succFileText += "\n\n";
successFileText += "\n\n";
} else {
succFileText += "\n";
successFileText += "\n";
}
}
});
@ -191,7 +194,11 @@ const genUploadedLabel = (responseText: string, protyle: IProtyle) => {
}
}
// 避免插入代码块中,其次因为都要独立成块 https://github.com/siyuan-note/siyuan/issues/7607
insertHTML(succFileText, protyle, insertBlock);
insertHTML(successFileText, protyle, insertBlock);
// 粘贴图片后定位不准确 https://github.com/siyuan-note/siyuan/issues/13336
setTimeout(() => {
scrollCenter(protyle, undefined, false, "smooth");
}, hasImage ? 0 : Constants.TIMEOUT_LOAD);
};
export const uploadLocalFiles = (files: string[], protyle: IProtyle, isUpload: boolean) => {

View file

@ -300,6 +300,7 @@ export const getLocalStorage = (cb: () => void) => {
replaceTypes: Object.assign({}, Constants.SIYUAN_DEFAULT_REPLACETYPES),
};
defaultStorage[Constants.LOCAL_ZOOM] = 1;
defaultStorage[Constants.LOCAL_MOVE_PATH] = {keys: [], k: ""};
[Constants.LOCAL_EXPORTIMG, Constants.LOCAL_SEARCHKEYS, Constants.LOCAL_PDFTHEME, Constants.LOCAL_BAZAAR,
Constants.LOCAL_EXPORTWORD, Constants.LOCAL_EXPORTPDF, Constants.LOCAL_DOCINFO, Constants.LOCAL_FONTSTYLES,
@ -307,7 +308,7 @@ export const getLocalStorage = (cb: () => void) => {
Constants.LOCAL_PLUGINTOPUNPIN, Constants.LOCAL_SEARCHASSET, Constants.LOCAL_FLASHCARD,
Constants.LOCAL_DIALOGPOSITION, Constants.LOCAL_SEARCHUNREF, Constants.LOCAL_HISTORY,
Constants.LOCAL_OUTLINE, Constants.LOCAL_FILEPOSITION, Constants.LOCAL_FILESPATHS, Constants.LOCAL_IMAGES,
Constants.LOCAL_PLUGIN_DOCKS, Constants.LOCAL_EMOJIS].forEach((key) => {
Constants.LOCAL_PLUGIN_DOCKS, Constants.LOCAL_EMOJIS, Constants.LOCAL_MOVE_PATH].forEach((key) => {
if (typeof response.data[key] === "string") {
try {
const parseData = JSON.parse(response.data[key]);

View file

@ -1,14 +1,17 @@
import {hideElements} from "../ui/hideElements";
import {isSupportCSSHL} from "../render/searchMarkRender";
export const destroy = (protyle: IProtyle) => {
if (!protyle) {
return;
}
hideElements(["util"], protyle);
protyle.highlight.markHL.clear();
protyle.highlight.mark.clear();
protyle.highlight.ranges = [];
protyle.highlight.rangeIndex = 0;
if (isSupportCSSHL()) {
protyle.highlight.markHL.clear();
protyle.highlight.mark.clear();
protyle.highlight.ranges = [];
protyle.highlight.rangeIndex = 0;
}
protyle.observer?.disconnect();
protyle.observerLoad?.disconnect();
protyle.element.classList.remove("protyle");

View file

@ -1132,15 +1132,28 @@ export const dropEvent = (protyle: IProtyle, editorElement: HTMLElement) => {
blockElement.setAttribute("updated", newUpdated);
}
} else {
for (let i = 0; i < ids.length; i++) {
if (ids[i]) {
await fetchSyncPost("/api/filetree/doc2Heading", {
srcID: ids[i],
after: targetElement.classList.contains("dragover__bottom"),
targetID: targetElement.getAttribute("data-node-id"),
});
if (targetElement.classList.contains("dragover__bottom")) {
for (let i = ids.length - 1; i > -1; i--) {
if (ids[i]) {
await fetchSyncPost("/api/filetree/doc2Heading", {
srcID: ids[i],
after: true,
targetID: targetElement.getAttribute("data-node-id"),
});
}
}
} else {
for (let i = 0; i < ids.length; i++) {
if (ids[i]) {
await fetchSyncPost("/api/filetree/doc2Heading", {
srcID: ids[i],
after: false,
targetID: targetElement.getAttribute("data-node-id"),
});
}
}
}
fetchPost("/api/filetree/getDoc", {
id: protyle.block.id,
size: window.siyuan.config.editor.dynamicLoadBlocks,

View file

@ -502,6 +502,7 @@ export const paste = async (protyle: IProtyle, event: (ClipboardEvent | DragEven
return;
} else if (files && files.length > 0) {
uploadFiles(protyle, files);
return;
} else if (textPlain.trim() !== "" && files && files.length === 0) {
if (range.toString() !== "") {
const firstLine = textPlain.split("\n")[0];

View file

@ -46,7 +46,7 @@ export const reloadProtyle = (protyle: IProtyle, focus: boolean, updateReadonly?
}, response => {
protyle.options.backlinkData = isMention ? response.data.backmentions : response.data.backlinks;
renderBacklink(protyle, protyle.options.backlinkData);
searchMarkRender(protyle, keyword.split(" "));
searchMarkRender(protyle, response.data.keywords);
});
}
} else {

View file

@ -737,7 +737,14 @@ export const clearTableCell = (protyle: IProtyle, tableBlockElement: HTMLElement
}
});
tableSelectElement.removeAttribute("style");
if (getSelection().rangeCount>0) {
const range = getSelection().getRangeAt(0);
if (tableBlockElement.contains(range.startContainer)) {
range.insertNode(document.createElement("wbr"));
}
}
const oldHTML = tableBlockElement.outerHTML;
tableBlockElement.querySelector("wbr")?.remove();
tableBlockElement.setAttribute("updated", dayjs().format("YYYYMMDDHHmmss"));
selectCellElements.forEach(item => {
item.innerHTML = "";

View file

@ -1482,7 +1482,14 @@ export class WYSIWYG {
}
});
tableSelectElement.removeAttribute("style");
if (getSelection().rangeCount>0) {
const range = getSelection().getRangeAt(0);
if (nodeElement.contains(range.startContainer)) {
range.insertNode(document.createElement("wbr"));
}
}
const oldHTML = nodeElement.outerHTML;
nodeElement.querySelector("wbr")?.remove();
nodeElement.setAttribute("updated", dayjs().format("YYYYMMDDHHmmss"));
selectCellElements.forEach((item, index) => {
if (index === 0 || !item.previousElementSibling ||

View file

@ -12,6 +12,7 @@ import {hasClosestByClassName} from "../protyle/util/hasClosest";
import {addClearButton} from "../util/addClearButton";
import {isPaidUser} from "../util/needSubscribe";
import {showMessage} from "../dialog/message";
import {saveAssetKeyList} from "./toggleHistory";
export const openSearchAsset = (element: Element, isStick: boolean) => {
/// #if !MOBILE
@ -108,18 +109,7 @@ export const openSearchAsset = (element: Element, isStick: boolean) => {
assetInputEvent(element, localSearch);
});
searchInputElement.addEventListener("blur", () => {
if (!searchInputElement.value) {
return;
}
let list: string[] = window.siyuan.storage[Constants.LOCAL_SEARCHASSET].keys;
list.splice(0, 0, searchInputElement.value);
list = Array.from(new Set(list));
if (list.length > window.siyuan.config.search.limit) {
list.splice(window.siyuan.config.search.limit, list.length - window.siyuan.config.search.limit);
}
window.siyuan.storage[Constants.LOCAL_SEARCHASSET].k = searchInputElement.value;
window.siyuan.storage[Constants.LOCAL_SEARCHASSET].keys = list;
setStorageVal(Constants.LOCAL_SEARCHASSET, window.siyuan.storage[Constants.LOCAL_SEARCHASSET]);
saveAssetKeyList(searchInputElement);
});
assetInputEvent(element, localSearch);
addClearButton({
@ -242,75 +232,6 @@ export const assetInputEvent = (element: Element, localSearch?: ISearchAssetOpti
}, Constants.TIMEOUT_INPUT);
};
export const toggleAssetHistory = (assetElement: Element) => {
const keys = window.siyuan.storage[Constants.LOCAL_SEARCHASSET].keys;
if (!keys || keys.length === 0) {
return;
}
const menu = new Menu("search-asset-history");
if (menu.isOpen) {
return;
}
menu.element.classList.add("b3-menu--list");
menu.addItem({
iconHTML: "",
label: window.siyuan.languages.clearHistory,
click() {
window.siyuan.storage[Constants.LOCAL_SEARCHASSET].keys = [];
setStorageVal(Constants.LOCAL_SEARCHASSET, window.siyuan.storage[Constants.LOCAL_SEARCHASSET]);
}
});
const separatorElement = menu.addSeparator(1);
let current = true;
const assetInputElement = assetElement.querySelector("#searchAssetInput") as HTMLInputElement;
keys.forEach((s: string) => {
if (s !== assetInputElement.value && s) {
const menuItem = menu.addItem({
iconHTML: "",
label: escapeHtml(s),
action: "iconCloseRound",
bind(element) {
element.addEventListener("click", (itemEvent) => {
if (hasClosestByClassName(itemEvent.target as Element, "b3-menu__action")) {
keys.find((item: string, index: number) => {
if (item === s) {
keys.splice(index, 1);
return true;
}
});
window.siyuan.storage[Constants.LOCAL_SEARCHASSET].keys = keys;
setStorageVal(Constants.LOCAL_SEARCHASSET, window.siyuan.storage[Constants.LOCAL_SEARCHASSET]);
if (element.previousElementSibling?.classList.contains("b3-menu__separator") && !element.nextElementSibling) {
window.siyuan.menus.menu.remove();
} else {
element.remove();
}
} else {
assetInputElement.value = element.textContent;
assetInputEvent(assetElement);
window.siyuan.menus.menu.remove();
}
itemEvent.preventDefault();
itemEvent.stopPropagation();
});
}
});
if (current) {
menuItem.classList.add("b3-menu__item--current");
}
current = false;
}
});
if (current) {
separatorElement.remove();
}
const rect = assetInputElement.getBoundingClientRect();
menu.open({
x: rect.left,
y: rect.bottom
});
};
export const renderPreview = (element: Element, id: string, query: string, queryMethod: number) => {
fetchPost("/api/search/getAssetContent", {id, query, queryMethod}, (response) => {
element.innerHTML = `<p style="white-space: pre-wrap;">${response.data.assetContent.content}</p>`;

View file

@ -0,0 +1,249 @@
import {Constants} from "../constants";
import {Menu} from "../plugin/Menu";
import {setStorageVal} from "../protyle/util/compatibility";
import {escapeHtml} from "../util/escape";
import {hasClosestByClassName} from "../protyle/util/hasClosest";
import {Protyle} from "../protyle";
import {assetInputEvent} from "./assets";
/// #if MOBILE
import {updateSearchResult} from "../mobile/menu/search";
/// #else
import {inputEvent} from "./util";
/// #endif
export const toggleReplaceHistory = (replaceInputElement: HTMLInputElement) => {
const list = window.siyuan.storage[Constants.LOCAL_SEARCHKEYS];
if (!list.replaceKeys || list.replaceKeys.length === 0) {
return;
}
const menu = new Menu("search-replace-history");
if (menu.isOpen) {
return;
}
menu.element.classList.add("b3-menu--list");
menu.addItem({
iconHTML: "",
label: window.siyuan.languages.clearHistory,
click() {
window.siyuan.storage[Constants.LOCAL_SEARCHKEYS].replaceKeys = [];
setStorageVal(Constants.LOCAL_SEARCHKEYS, window.siyuan.storage[Constants.LOCAL_SEARCHKEYS]);
}
});
const separatorElement = menu.addSeparator(1);
let current = true;
list.replaceKeys.forEach((s: string) => {
if (s !== replaceInputElement.value && s) {
const menuItem = menu.addItem({
iconHTML: "",
label: escapeHtml(s),
action: "iconCloseRound",
bind(element) {
element.addEventListener("click", (itemEvent) => {
if (hasClosestByClassName(itemEvent.target as Element, "b3-menu__action")) {
list.replaceKeys.find((item: string, index: number) => {
if (item === s) {
list.replaceKeys.splice(index, 1);
return true;
}
});
window.siyuan.storage[Constants.LOCAL_SEARCHKEYS].replaceKeys = list.replaceKeys;
setStorageVal(Constants.LOCAL_SEARCHKEYS, window.siyuan.storage[Constants.LOCAL_SEARCHKEYS]);
if (element.previousElementSibling?.classList.contains("b3-menu__separator") && !element.nextElementSibling) {
window.siyuan.menus.menu.remove();
} else {
element.remove();
}
} else {
replaceInputElement.value = element.textContent;
window.siyuan.menus.menu.remove();
}
itemEvent.preventDefault();
itemEvent.stopPropagation();
});
}
});
if (current) {
menuItem.classList.add("b3-menu__item--current");
}
current = false;
}
});
if (current) {
separatorElement.remove();
}
const rect = replaceInputElement.previousElementSibling.getBoundingClientRect();
menu.open({
x: rect.left,
y: rect.bottom
});
};
export const toggleSearchHistory = (searchElement: Element, config: Config.IUILayoutTabSearchConfig, edit: Protyle) => {
const list = window.siyuan.storage[Constants.LOCAL_SEARCHKEYS];
if (!list.keys || list.keys.length === 0) {
return;
}
const menu = new Menu("search-history");
if (menu.isOpen) {
return;
}
menu.element.classList.add("b3-menu--list");
menu.addItem({
iconHTML: "",
label: window.siyuan.languages.clearHistory,
click() {
window.siyuan.storage[Constants.LOCAL_SEARCHKEYS].keys = [];
setStorageVal(Constants.LOCAL_SEARCHKEYS, window.siyuan.storage[Constants.LOCAL_SEARCHKEYS]);
}
});
const separatorElement = menu.addSeparator(1);
let current = true;
const searchInputElement = searchElement.querySelector("#searchInput, #toolbarSearch") as HTMLInputElement;
list.keys.forEach((s: string) => {
if (s !== searchInputElement.value && s) {
const menuItem = menu.addItem({
iconHTML: "",
label: escapeHtml(s),
action: "iconCloseRound",
bind(element) {
element.addEventListener("click", (itemEvent) => {
if (hasClosestByClassName(itemEvent.target as Element, "b3-menu__action")) {
list.keys.find((item: string, index: number) => {
if (item === s) {
list.keys.splice(index, 1);
return true;
}
});
window.siyuan.storage[Constants.LOCAL_SEARCHKEYS].keys = list.keys;
setStorageVal(Constants.LOCAL_SEARCHKEYS, window.siyuan.storage[Constants.LOCAL_SEARCHKEYS]);
if (element.previousElementSibling?.classList.contains("b3-menu__separator") && !element.nextElementSibling) {
window.siyuan.menus.menu.remove();
} else {
element.remove();
}
} else {
searchInputElement.value = element.textContent;
config.page = 1;
/// #if MOBILE
updateSearchResult(config, searchElement, true);
/// #else
inputEvent(searchElement, config, edit, true);
/// #endif
window.siyuan.menus.menu.remove();
}
itemEvent.preventDefault();
itemEvent.stopPropagation();
});
}
});
if (current) {
menuItem.classList.add("b3-menu__item--current");
}
current = false;
}
});
if (current) {
separatorElement.remove();
}
const rect = searchInputElement.previousElementSibling.getBoundingClientRect();
menu.open({
x: rect.left,
y: rect.bottom
});
};
export const toggleAssetHistory = (assetElement: Element) => {
const keys = window.siyuan.storage[Constants.LOCAL_SEARCHASSET].keys;
if (!keys || keys.length === 0) {
return;
}
const menu = new Menu("search-asset-history");
if (menu.isOpen) {
return;
}
menu.element.classList.add("b3-menu--list");
menu.addItem({
iconHTML: "",
label: window.siyuan.languages.clearHistory,
click() {
window.siyuan.storage[Constants.LOCAL_SEARCHASSET].keys = [];
setStorageVal(Constants.LOCAL_SEARCHASSET, window.siyuan.storage[Constants.LOCAL_SEARCHASSET]);
}
});
const separatorElement = menu.addSeparator(1);
let current = true;
const assetInputElement = assetElement.querySelector("#searchAssetInput") as HTMLInputElement;
keys.forEach((s: string) => {
if (s !== assetInputElement.value && s) {
const menuItem = menu.addItem({
iconHTML: "",
label: escapeHtml(s),
action: "iconCloseRound",
bind(element) {
element.addEventListener("click", (itemEvent) => {
if (hasClosestByClassName(itemEvent.target as Element, "b3-menu__action")) {
keys.find((item: string, index: number) => {
if (item === s) {
keys.splice(index, 1);
return true;
}
});
window.siyuan.storage[Constants.LOCAL_SEARCHASSET].keys = keys;
setStorageVal(Constants.LOCAL_SEARCHASSET, window.siyuan.storage[Constants.LOCAL_SEARCHASSET]);
if (element.previousElementSibling?.classList.contains("b3-menu__separator") && !element.nextElementSibling) {
window.siyuan.menus.menu.remove();
} else {
element.remove();
}
} else {
assetInputElement.value = element.textContent;
assetInputEvent(assetElement);
window.siyuan.menus.menu.remove();
}
itemEvent.preventDefault();
itemEvent.stopPropagation();
});
}
});
if (current) {
menuItem.classList.add("b3-menu__item--current");
}
current = false;
}
});
if (current) {
separatorElement.remove();
}
const rect = assetInputElement.previousElementSibling.getBoundingClientRect();
menu.open({
x: rect.left,
y: rect.bottom
});
};
export const saveKeyList = (type: "keys" | "replaceKeys", value: string) => {
let list: string[] = window.siyuan.storage[Constants.LOCAL_SEARCHKEYS][type];
list.splice(0, 0, value);
list = Array.from(new Set(list));
if (list.length > window.siyuan.config.search.limit) {
list.splice(window.siyuan.config.search.limit, list.length - window.siyuan.config.search.limit);
}
// new Set 后需重新赋值
window.siyuan.storage[Constants.LOCAL_SEARCHKEYS][type] = list;
setStorageVal(Constants.LOCAL_SEARCHKEYS, window.siyuan.storage[Constants.LOCAL_SEARCHKEYS]);
};
export const saveAssetKeyList = (inputElement:HTMLInputElement) => {
if (!inputElement.value) {
return;
}
let list: string[] = window.siyuan.storage[Constants.LOCAL_SEARCHASSET].keys;
list.splice(0, 0, inputElement.value);
list = Array.from(new Set(list));
if (list.length > window.siyuan.config.search.limit) {
list.splice(window.siyuan.config.search.limit, list.length - window.siyuan.config.search.limit);
}
window.siyuan.storage[Constants.LOCAL_SEARCHASSET].k = inputElement.value;
window.siyuan.storage[Constants.LOCAL_SEARCHASSET].keys = list;
setStorageVal(Constants.LOCAL_SEARCHASSET, window.siyuan.storage[Constants.LOCAL_SEARCHASSET]);
};

View file

@ -43,165 +43,14 @@ import {
openSearchAsset,
renderNextAssetMark,
renderPreview,
toggleAssetHistory
} from "./assets";
import {resize} from "../protyle/util/resize";
import {Menu} from "../plugin/Menu";
import {addClearButton} from "../util/addClearButton";
import {checkFold} from "../util/noRelyPCFunction";
import {getUnRefList, openSearchUnRef, unRefMoreMenu} from "./unRef";
import {getDefaultType} from "./getDefault";
import {isSupportCSSHL, searchMarkRender} from "../protyle/render/searchMarkRender";
export const toggleReplaceHistory = (searchElement: Element) => {
const list = window.siyuan.storage[Constants.LOCAL_SEARCHKEYS];
if (!list.replaceKeys || list.replaceKeys.length === 0) {
return;
}
const menu = new Menu("search-replace-history");
if (menu.isOpen) {
return;
}
menu.element.classList.add("b3-menu--list");
menu.addItem({
iconHTML: "",
label: window.siyuan.languages.clearHistory,
click() {
window.siyuan.storage[Constants.LOCAL_SEARCHKEYS].replaceKeys = [];
setStorageVal(Constants.LOCAL_SEARCHKEYS, window.siyuan.storage[Constants.LOCAL_SEARCHKEYS]);
}
});
const separatorElement = menu.addSeparator(1);
let current = true;
const replaceInputElement = searchElement.querySelector("#replaceInput") as HTMLInputElement;
list.replaceKeys.forEach((s: string) => {
if (s !== replaceInputElement.value && s) {
const menuItem = menu.addItem({
iconHTML: "",
label: escapeHtml(s),
action: "iconCloseRound",
bind(element) {
element.addEventListener("click", (itemEvent) => {
if (hasClosestByClassName(itemEvent.target as Element, "b3-menu__action")) {
list.replaceKeys.find((item: string, index: number) => {
if (item === s) {
list.replaceKeys.splice(index, 1);
return true;
}
});
window.siyuan.storage[Constants.LOCAL_SEARCHKEYS].replaceKeys = list.replaceKeys;
setStorageVal(Constants.LOCAL_SEARCHKEYS, window.siyuan.storage[Constants.LOCAL_SEARCHKEYS]);
if (element.previousElementSibling?.classList.contains("b3-menu__separator") && !element.nextElementSibling) {
window.siyuan.menus.menu.remove();
} else {
element.remove();
}
} else {
replaceInputElement.value = element.textContent;
window.siyuan.menus.menu.remove();
}
itemEvent.preventDefault();
itemEvent.stopPropagation();
});
}
});
if (current) {
menuItem.classList.add("b3-menu__item--current");
}
current = false;
}
});
if (current) {
separatorElement.remove();
}
const rect = replaceInputElement.getBoundingClientRect();
menu.open({
x: rect.left,
y: rect.bottom
});
};
export const toggleSearchHistory = (searchElement: Element, config: Config.IUILayoutTabSearchConfig, edit: Protyle) => {
const list = window.siyuan.storage[Constants.LOCAL_SEARCHKEYS];
if (!list.keys || list.keys.length === 0) {
return;
}
const menu = new Menu("search-history");
if (menu.isOpen) {
return;
}
menu.element.classList.add("b3-menu--list");
menu.addItem({
iconHTML: "",
label: window.siyuan.languages.clearHistory,
click() {
window.siyuan.storage[Constants.LOCAL_SEARCHKEYS].keys = [];
setStorageVal(Constants.LOCAL_SEARCHKEYS, window.siyuan.storage[Constants.LOCAL_SEARCHKEYS]);
}
});
const separatorElement = menu.addSeparator(1);
let current = true;
const searchInputElement = searchElement.querySelector("#searchInput") as HTMLInputElement;
list.keys.forEach((s: string) => {
if (s !== searchInputElement.value && s) {
const menuItem = menu.addItem({
iconHTML: "",
label: escapeHtml(s),
action: "iconCloseRound",
bind(element) {
element.addEventListener("click", (itemEvent) => {
if (hasClosestByClassName(itemEvent.target as Element, "b3-menu__action")) {
list.keys.find((item: string, index: number) => {
if (item === s) {
list.keys.splice(index, 1);
return true;
}
});
window.siyuan.storage[Constants.LOCAL_SEARCHKEYS].keys = list.keys;
setStorageVal(Constants.LOCAL_SEARCHKEYS, window.siyuan.storage[Constants.LOCAL_SEARCHKEYS]);
if (element.previousElementSibling?.classList.contains("b3-menu__separator") && !element.nextElementSibling) {
window.siyuan.menus.menu.remove();
} else {
element.remove();
}
} else {
searchInputElement.value = element.textContent;
config.page = 1;
inputEvent(searchElement, config, edit, true);
window.siyuan.menus.menu.remove();
}
itemEvent.preventDefault();
itemEvent.stopPropagation();
});
}
});
if (current) {
menuItem.classList.add("b3-menu__item--current");
}
current = false;
}
});
if (current) {
separatorElement.remove();
}
const rect = searchInputElement.getBoundingClientRect();
menu.open({
x: rect.left,
y: rect.bottom
});
};
const saveKeyList = (type: "keys" | "replaceKeys", value: string) => {
let list: string[] = window.siyuan.storage[Constants.LOCAL_SEARCHKEYS][type];
list.splice(0, 0, value);
list = Array.from(new Set(list));
if (list.length > window.siyuan.config.search.limit) {
list.splice(window.siyuan.config.search.limit, list.length - window.siyuan.config.search.limit);
}
// new Set 后需重新赋值
window.siyuan.storage[Constants.LOCAL_SEARCHKEYS][type] = list;
setStorageVal(Constants.LOCAL_SEARCHKEYS, window.siyuan.storage[Constants.LOCAL_SEARCHKEYS]);
};
import {saveKeyList, toggleAssetHistory, toggleReplaceHistory, toggleSearchHistory} from "./toggleHistory";
export const openGlobalSearch = (app: App, text: string, replace: boolean, searchData?: Config.IUILayoutTabSearchConfig) => {
text = text.trim();
@ -296,7 +145,7 @@ export const genSearch = (app: App, config: Config.IUILayoutTabSearchConfig, ele
</div>
<div class="b3-form__icon search__header">
<div style="position: relative" class="fn__flex-1">
<span class="search__history-icon ariaLabel" id="searchHistoryBtn" aria-label="${updateHotkeyTip("")}">
<span class="search__history-icon ariaLabel" id="searchHistoryBtn" aria-label="${updateHotkeyTip("")}">
<svg data-menu="true" class="b3-form__icon-icon"><use xlink:href="#iconSearch"></use></svg>
<svg class="search__arrowdown"><use xlink:href="#iconDown"></use></svg>
</span>
@ -908,7 +757,7 @@ export const genSearch = (app: App, config: Config.IUILayoutTabSearchConfig, ele
event.preventDefault();
return;
} else if (target.id === "replaceHistoryBtn") {
toggleReplaceHistory(element);
toggleReplaceHistory(element.querySelector("#replaceInput"));
event.stopPropagation();
event.preventDefault();
return;

View file

@ -1349,6 +1349,10 @@ declare namespace Config {
* - `3`: Completely manual synchronization
*/
mode: number;
/**
* Synchronization interval (unit: seconds)
*/
interval: number;
/**
* Whether to enable synchronization perception
*/

View file

@ -201,7 +201,28 @@ export const addGA = () => {
export const setInlineStyle = (set = true) => {
const height = Math.floor(window.siyuan.config.editor.fontSize * 1.625);
let style = `.b3-typography, .protyle-wysiwyg, .protyle-title {font-size:${window.siyuan.config.editor.fontSize}px !important}
let style;
if (window.siyuan.config.editor.fontFamily) {
style = `@font-face {
font-family: "Number Glyphs";
src: local("${window.siyuan.config.editor.fontFamily}");
unicode-range: U+30-39;
}
.b3-typography:not(.b3-typography--default), .protyle-wysiwyg, .protyle-title {font-family: "Number Glyphs", "SiYuan Emojis", "${window.siyuan.config.editor.fontFamily}", var(--b3-font-family-protyle)}`;
} else {
style = `@font-face {
font-family: "Number Glyphs";
src: local("Helvetica Neue"),
local("Luxi Sans"),
local("DejaVu Sans"),
local("Hiragino Sans GB"),
local("Segoe UI"),
local("Microsoft Yahei"),
local("sans-serif");
unicode-range: U+30-39;
}`;
}
style += `.b3-typography, .protyle-wysiwyg, .protyle-title {font-size:${window.siyuan.config.editor.fontSize}px !important}
.b3-typography code:not(.hljs), .protyle-wysiwyg span[data-type~=code] { font-variant-ligatures: ${window.siyuan.config.editor.codeLigatures ? "normal" : "none"} }
.li > .protyle-action {height:${height + 8}px;line-height: ${height + 8}px}
.protyle-wysiwyg [data-node-id].li > .protyle-action ~ .h1, .protyle-wysiwyg [data-node-id].li > .protyle-action ~ .h2, .protyle-wysiwyg [data-node-id].li > .protyle-action ~ .h3, .protyle-wysiwyg [data-node-id].li > .protyle-action ~ .h4, .protyle-wysiwyg [data-node-id].li > .protyle-action ~ .h5, .protyle-wysiwyg [data-node-id].li > .protyle-action ~ .h6 {line-height:${height + 8}px;}
@ -218,9 +239,6 @@ export const setInlineStyle = (set = true) => {
.protyle-wysiwyg [data-node-id] {${window.siyuan.config.editor.justify ? " text-align: justify;" : ""}}
.protyle-wysiwyg .li {min-height:${height + 8}px}
.protyle-gutters button svg {height:${height}px}`;
if (window.siyuan.config.editor.fontFamily) {
style += `\n.b3-typography:not(.b3-typography--default), .protyle-wysiwyg, .protyle-title {font-family: "${window.siyuan.config.editor.fontFamily}", var(--b3-font-family-protyle)}`;
}
// pad 端菜单移除显示,如工作空间
if ("ontouchend" in document) {
style += "\n.b3-menu .b3-menu__action {opacity: 0.68;}";

View file

@ -59,7 +59,7 @@ const focusStack = async (app: App, stack: IBackStack) => {
tab,
blockId: stack.zoomId || stack.protyle.block.rootID,
rootId: stack.protyle.block.rootID,
action: stack.zoomId ? [Constants.CB_GET_FOCUS, Constants.CB_GET_ALL] : [Constants.CB_GET_FOCUS]
action: stack.zoomId ? [Constants.CB_GET_FOCUS, Constants.CB_GET_ALL, Constants.CB_GET_UNUNDO] : [Constants.CB_GET_FOCUS, Constants.CB_GET_UNUNDO]
});
tab.addModel(editor);
}

View file

@ -10,7 +10,10 @@ import {Constants} from "../constants";
import {ipcRenderer} from "electron";
/// #endif
import {showMessage} from "../dialog/message";
import {isOnlyMeta} from "../protyle/util/compatibility";
import {isOnlyMeta, setStorageVal, updateHotkeyTip} from "../protyle/util/compatibility";
import {matchHotKey} from "../protyle/util/hotKey";
import {Menu} from "../plugin/Menu";
import {hasClosestByClassName} from "../protyle/util/hasClosest";
export const showFileInFolder = (filePath: string) => {
/// #if !BROWSER
@ -154,8 +157,11 @@ export const movePathTo = (cb: (toPath: string[], toNotebook: string[]) => void,
title: `${title || window.siyuan.languages.move}
<div style="max-height: 16px;overflow: auto;line-height: 14px;-webkit-mask-image: linear-gradient(to top, rgba(0, 0, 0, 0) 0, #000 6px);padding-bottom: 4px;margin-bottom: -4px" class="ft__smaller ft__on-surface fn__hidescrollbar"></div>`,
content: `<div class="b3-form__icon" style="margin: 8px">
<svg class="b3-form__icon-icon"><use xlink:href="#iconSearch"></use></svg>
<input class="b3-text-field fn__block b3-form__icon-input" value="" placeholder="${window.siyuan.languages.search}">
<span data-menu="true" class="b3-form__icon-list fn__a b3-tooltips b3-tooltips__s" aria-label="${updateHotkeyTip("")}">
<svg class="svg--mid"><use xlink:href="#iconSearch"></use></svg>
<svg class="svg--smaller"><use xlink:href="#iconDown"></use></svg>
</span>
<input class="b3-text-field fn__block" style="padding-left: 42px;" value="" placeholder="${window.siyuan.languages.search}">
</div>
<ul id="foldList" class="fn__flex-1 fn__none b3-list b3-list--background${isMobile() ? " b3-list--mobile" : ""}" style="overflow: auto;position: relative"></ul>
<div id="foldTree" class="fn__flex-1${isMobile() ? " b3-list--mobile" : ""}" style="overflow: auto;position: relative"></div>
@ -205,8 +211,9 @@ export const movePathTo = (cb: (toPath: string[], toNotebook: string[]) => void,
}, flashcard);
const inputElement = dialog.element.querySelector(".b3-text-field") as HTMLInputElement;
inputElement.value = window.siyuan.storage[Constants.LOCAL_MOVE_PATH].k;
/// #if !MOBILE
inputElement.focus();
inputElement.select();
/// #endif
const inputEvent = (event?: InputEvent) => {
if (event && event.isComposing) {
@ -250,6 +257,74 @@ export const movePathTo = (cb: (toPath: string[], toNotebook: string[]) => void,
searchListElement.innerHTML = fileHTML;
});
};
const toggleMovePathHistory = () => {
const keys = window.siyuan.storage[Constants.LOCAL_MOVE_PATH].keys;
if (!keys || keys.length === 0) {
return;
}
const menu = new Menu("move-path-history");
if (menu.isOpen) {
return;
}
menu.element.classList.add("b3-menu--list");
menu.addItem({
iconHTML: "",
label: window.siyuan.languages.clearHistory,
click() {
window.siyuan.storage[Constants.LOCAL_MOVE_PATH].keys = [];
setStorageVal(Constants.LOCAL_MOVE_PATH, window.siyuan.storage[Constants.LOCAL_MOVE_PATH]);
}
});
const separatorElement = menu.addSeparator(1);
let current = true;
keys.forEach((s: string) => {
if (s !== inputElement.value && s) {
const menuItem = menu.addItem({
iconHTML: "",
label: escapeHtml(s),
action: "iconCloseRound",
bind(element) {
element.addEventListener("click", (itemEvent) => {
if (hasClosestByClassName(itemEvent.target as Element, "b3-menu__action")) {
keys.find((item: string, index: number) => {
if (item === s) {
keys.splice(index, 1);
return true;
}
});
window.siyuan.storage[Constants.LOCAL_MOVE_PATH].keys = keys;
setStorageVal(Constants.LOCAL_MOVE_PATH, window.siyuan.storage[Constants.LOCAL_MOVE_PATH]);
if (element.previousElementSibling?.classList.contains("b3-menu__separator") && !element.nextElementSibling) {
window.siyuan.menus.menu.remove();
} else {
element.remove();
}
} else {
inputElement.value = element.textContent;
inputEvent();
window.siyuan.menus.menu.remove();
}
itemEvent.preventDefault();
itemEvent.stopPropagation();
});
}
});
if (current) {
menuItem.classList.add("b3-menu__item--current");
}
current = false;
}
});
if (current) {
separatorElement.remove();
}
const rect = inputElement.getBoundingClientRect();
menu.open({
x: rect.left,
y: rect.bottom
});
};
inputEvent();
inputElement.addEventListener("compositionend", (event: InputEvent) => {
inputEvent(event);
@ -257,11 +332,33 @@ export const movePathTo = (cb: (toPath: string[], toNotebook: string[]) => void,
inputElement.addEventListener("input", (event: InputEvent) => {
inputEvent(event);
});
inputElement.addEventListener("blur", () => {
if (!inputElement.value) {
return;
}
let list: string[] = window.siyuan.storage[Constants.LOCAL_MOVE_PATH].keys;
list.splice(0, 0, inputElement.value);
list = Array.from(new Set(list));
if (list.length > window.siyuan.config.search.limit) {
list.splice(window.siyuan.config.search.limit, list.length - window.siyuan.config.search.limit);
}
window.siyuan.storage[Constants.LOCAL_MOVE_PATH].k = inputElement.value;
window.siyuan.storage[Constants.LOCAL_MOVE_PATH].keys = list;
setStorageVal(Constants.LOCAL_MOVE_PATH, window.siyuan.storage[Constants.LOCAL_MOVE_PATH]);
});
const lineHeight = 28;
inputElement.addEventListener("keydown", (event: KeyboardEvent) => {
if (event.isComposing) {
return;
}
if (matchHotKey("⌥↓", event)) {
event.stopPropagation();
toggleMovePathHistory();
return;
}
if (window.siyuan.menus.menu.element.getAttribute("data-name") === "move-path-history") {
return;
}
const currentPanelElement = searchListElement.classList.contains("fn__none") ? searchTreeElement : searchListElement;
const currentItemElements = currentPanelElement.querySelectorAll(".b3-list-item--focus");
if (currentItemElements.length === 0) {
@ -424,6 +521,11 @@ export const movePathTo = (cb: (toPath: string[], toNotebook: string[]) => void,
event.preventDefault();
event.stopPropagation();
break;
} else if (target.classList.contains("b3-form__icon-list")) {
toggleMovePathHistory();
event.preventDefault();
event.stopPropagation();
break;
} else if (target.classList.contains("b3-button--text")) {
const currentPanelElement = searchListElement.classList.contains("fn__none") ? searchTreeElement : searchListElement;
const currentItemElements = currentPanelElement.querySelectorAll(".b3-list-item--focus");

File diff suppressed because one or more lines are too long

View file

@ -26,6 +26,7 @@ import (
"github.com/88250/lute/html"
"github.com/gin-gonic/gin"
"github.com/siyuan-note/logging"
"github.com/siyuan-note/siyuan/kernel/filesys"
"github.com/siyuan-note/siyuan/kernel/model"
"github.com/siyuan-note/siyuan/kernel/util"
)
@ -320,7 +321,7 @@ func getContentWordCount(c *gin.Context) {
}
content := arg["content"].(string)
ret.Data = model.ContentStat(content)
ret.Data = filesys.ContentStat(content)
}
func getBlocksWordCount(c *gin.Context) {
@ -337,7 +338,7 @@ func getBlocksWordCount(c *gin.Context) {
for _, id := range idsArg {
ids = append(ids, id.(string))
}
ret.Data = model.BlocksWordCount(ids)
ret.Data = filesys.BlocksWordCount(ids)
}
func getTreeStat(c *gin.Context) {
@ -350,7 +351,7 @@ func getTreeStat(c *gin.Context) {
}
id := arg["id"].(string)
ret.Data = model.StatTree(id)
ret.Data = filesys.StatTree(id)
}
func getDOMText(c *gin.Context) {

View file

@ -60,9 +60,10 @@ func getBackmentionDoc(c *gin.Context) {
if val, ok := arg["highlight"]; ok {
highlight = val.(bool)
}
backlinks := model.GetBackmentionDoc(defID, refTreeID, keyword, containChildren, highlight)
backlinks, keywords := model.GetBackmentionDoc(defID, refTreeID, keyword, containChildren, highlight)
ret.Data = map[string]interface{}{
"backmentions": backlinks,
"keywords": keywords,
}
}
@ -86,9 +87,10 @@ func getBacklinkDoc(c *gin.Context) {
if val, ok := arg["highlight"]; ok {
highlight = val.(bool)
}
backlinks := model.GetBacklinkDoc(defID, refTreeID, keyword, containChildren, highlight)
backlinks, keywords := model.GetBacklinkDoc(defID, refTreeID, keyword, containChildren, highlight)
ret.Data = map[string]interface{}{
"backlinks": backlinks,
"keywords": keywords,
}
}

View file

@ -237,6 +237,7 @@ func ServeAPI(ginServer *gin.Engine) {
ginServer.Handle("POST", "/api/cloud/getCloudSpace", model.CheckAuth, model.CheckAdminRole, getCloudSpace)
ginServer.Handle("POST", "/api/sync/setSyncEnable", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, setSyncEnable)
ginServer.Handle("POST", "/api/sync/setSyncInterval", model.CheckAuth, setSyncInterval)
ginServer.Handle("POST", "/api/sync/setSyncPerception", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, setSyncPerception)
ginServer.Handle("POST", "/api/sync/setSyncGenerateConflictDoc", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, setSyncGenerateConflictDoc)
ginServer.Handle("POST", "/api/sync/setSyncMode", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, setSyncMode)

View file

@ -541,6 +541,17 @@ func setSyncEnable(c *gin.Context) {
model.SetSyncEnable(enabled)
}
func setSyncInterval(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
interval := int(arg["interval"].(float64))
model.SetSyncInterval(interval)
}
func setSyncPerception(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)

View file

@ -80,6 +80,12 @@ func renderTemplate(c *gin.Context) {
return
}
if !util.IsAbsPathInWorkspace(p) {
ret.Code = -1
ret.Msg = "Path [" + p + "] is not in workspace"
return
}
preview := false
if previewArg := arg["preview"]; nil != previewArg {
preview = previewArg.(bool)

View file

@ -21,6 +21,7 @@ type Sync struct {
Enabled bool `json:"enabled"` // 是否开启同步
Perception bool `json:"perception"` // 是否开启感知
Mode int `json:"mode"` // 同步模式0未设置为兼容已有配置initConf 函数中会转换为 11自动2手动 https://github.com/siyuan-note/siyuan/issues/50893完全手动 https://github.com/siyuan-note/siyuan/issues/7295
Interval int `json:"interval"` // 自动同步间隔,单位:秒
Synced int64 `json:"synced"` // 最近同步时间
Stat string `json:"stat"` // 最近同步统计信息
GenerateConflictDoc bool `json:"generateConflictDoc"` // 云端同步冲突时是否生成冲突文档
@ -37,6 +38,7 @@ func NewSync() *Sync {
Mode: 1,
GenerateConflictDoc: false,
Provider: ProviderSiYuan,
Interval: 30,
}
}

223
kernel/filesys/stat.go Normal file
View file

@ -0,0 +1,223 @@
// SiYuan - Refactor your thinking
// Copyright (c) 2020-present, b3log.org
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package filesys
import (
"bytes"
"github.com/88250/lute"
"github.com/88250/lute/ast"
"github.com/88250/lute/parse"
"github.com/siyuan-note/siyuan/kernel/av"
"github.com/siyuan-note/siyuan/kernel/treenode"
"github.com/siyuan-note/siyuan/kernel/util"
)
func ContentStat(content string) (ret *util.BlockStatResult) {
luteEngine := util.NewLute()
return contentStat(content, luteEngine)
}
func contentStat(content string, luteEngine *lute.Lute) (ret *util.BlockStatResult) {
tree := luteEngine.BlockDOM2Tree(content)
runeCnt, wordCnt, linkCnt, imgCnt, refCnt := tree.Root.Stat()
return &util.BlockStatResult{
RuneCount: runeCnt,
WordCount: wordCnt,
LinkCount: linkCnt,
ImageCount: imgCnt,
RefCount: refCnt,
}
}
func StatBlock(id string) (ret *util.BlockStatResult) {
trees := LoadTrees([]string{id})
if 1 > len(trees) {
return
}
tree := trees[id]
if nil == tree {
return
}
node := treenode.GetNodeInTree(tree, id)
if nil == node {
return
}
if ast.NodeDocument == node.Type {
return statTree(tree)
}
runeCnt, wordCnt, linkCnt, imgCnt, refCnt := node.Stat()
ret = &util.BlockStatResult{
runeCnt,
wordCnt,
linkCnt,
imgCnt,
refCnt,
1,
}
return
}
func StatTree(id string) (ret *util.BlockStatResult) {
trees := LoadTrees([]string{id})
if 1 > len(trees) {
return
}
tree := trees[id]
if nil == tree {
return
}
return statTree(tree)
}
func statTree(tree *parse.Tree) (ret *util.BlockStatResult) {
blockCount := 0
var databaseBlockNodes []*ast.Node
ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
if !entering {
return ast.WalkContinue
}
if n.IsBlock() {
blockCount++
}
if ast.NodeAttributeView != n.Type {
return ast.WalkContinue
}
databaseBlockNodes = append(databaseBlockNodes, n)
return ast.WalkContinue
})
luteEngine := util.NewLute()
var dbRuneCnt, dbWordCnt, dbLinkCnt, dbImgCnt, dbRefCnt int
for _, n := range databaseBlockNodes {
if "" == n.AttributeViewID {
continue
}
attrView, _ := av.ParseAttributeView(n.AttributeViewID)
if nil == attrView {
continue
}
content := bytes.Buffer{}
for _, kValues := range attrView.KeyValues {
for _, v := range kValues.Values {
switch kValues.Key.Type {
case av.KeyTypeURL:
if v.IsEmpty() {
continue
}
dbLinkCnt++
content.WriteString(v.URL.Content)
case av.KeyTypeMAsset:
if v.IsEmpty() {
continue
}
for _, asset := range v.MAsset {
if av.AssetTypeImage == asset.Type {
dbImgCnt++
}
}
case av.KeyTypeBlock:
if v.IsEmpty() {
continue
}
if !v.IsDetached {
dbRefCnt++
}
content.WriteString(v.Block.Content)
case av.KeyTypeText:
if v.IsEmpty() {
continue
}
content.WriteString(v.Text.Content)
case av.KeyTypeNumber:
if v.IsEmpty() {
continue
}
v.Number.FormatNumber()
content.WriteString(v.Number.FormattedContent)
case av.KeyTypeEmail:
if v.IsEmpty() {
continue
}
content.WriteString(v.Email.Content)
case av.KeyTypePhone:
if v.IsEmpty() {
continue
}
content.WriteString(v.Phone.Content)
}
}
}
dbStat := contentStat(content.String(), luteEngine)
dbRuneCnt += dbStat.RuneCount
dbWordCnt += dbStat.WordCount
}
runeCnt, wordCnt, linkCnt, imgCnt, refCnt := tree.Root.Stat()
runeCnt += dbRuneCnt
wordCnt += dbWordCnt
linkCnt += dbLinkCnt
imgCnt += dbImgCnt
refCnt += dbRefCnt
return &util.BlockStatResult{
RuneCount: runeCnt,
WordCount: wordCnt,
LinkCount: linkCnt,
ImageCount: imgCnt,
RefCount: refCnt,
BlockCount: blockCount,
}
}
func BlocksWordCount(ids []string) (ret *util.BlockStatResult) {
ret = &util.BlockStatResult{}
trees := LoadTrees(ids)
for _, id := range ids {
tree := trees[id]
if nil == tree {
continue
}
node := treenode.GetNodeInTree(tree, id)
if nil == node {
continue
}
runeCnt, wordCnt, linkCnt, imgCnt, refCnt := node.Stat()
ret.RuneCount += runeCnt
ret.WordCount += wordCnt
ret.LinkCount += linkCnt
ret.ImageCount += imgCnt
ret.RefCount += refCnt
}
ret.BlockCount = len(ids)
return
}

View file

@ -14,23 +14,31 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package treenode
package filesys
import (
"math"
"text/template"
"time"
"unicode/utf8"
"github.com/88250/go-humanize"
"github.com/Masterminds/sprig/v3"
"github.com/araddon/dateparse"
"github.com/siyuan-note/logging"
"github.com/siyuan-note/siyuan/kernel/treenode"
"github.com/siyuan-note/siyuan/kernel/util"
"github.com/spf13/cast"
)
func BuiltInTemplateFuncs() (ret template.FuncMap) {
ret = sprig.TxtFuncMap()
// 因为安全原因移除一些函数 https://github.com/siyuan-note/siyuan/issues/13426
delete(ret, "env")
delete(ret, "expandenv")
delete(ret, "getHostByName")
ret["Weekday"] = util.Weekday
ret["WeekdayCN"] = util.WeekdayCN
ret["WeekdayCN2"] = util.WeekdayCN2
@ -42,9 +50,15 @@ func BuiltInTemplateFuncs() (ret template.FuncMap) {
ret["parseTime"] = parseTime
ret["FormatFloat"] = FormatFloat
ret["getHPathByID"] = getHPathByID
ret["statBlock"] = StatBlock
ret["runeLen"] = runeLen
return
}
func runeLen(s string) int {
return utf8.RuneCountInString(s)
}
func pow(a, b interface{}) int64 { return int64(math.Pow(cast.ToFloat64(a), cast.ToFloat64(b))) }
func powf(a, b interface{}) float64 { return math.Pow(cast.ToFloat64(a), cast.ToFloat64(b)) }
func log(a, b interface{}) int64 {
@ -67,7 +81,7 @@ func FormatFloat(format string, n float64) string {
}
func getHPathByID(id string) (ret string) {
bt := GetBlockTree(id)
bt := treenode.GetBlockTree(id)
if nil == bt {
return
}

View file

@ -9,8 +9,8 @@ require (
github.com/88250/clipboard v0.1.5
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.20241208103455-3223b6b5f502
github.com/88250/gulu v1.2.3-0.20241212012748-c4dc08fe45ec
github.com/88250/lute v1.7.7-0.20241213121436-8647e479e280
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
@ -30,7 +30,7 @@ require (
github.com/flopp/go-findfont v0.1.0
github.com/fsnotify/fsnotify v1.7.0
github.com/gabriel-vasile/mimetype v1.4.5
github.com/getsentry/sentry-go v0.29.1
github.com/getsentry/sentry-go v0.30.0
github.com/gin-contrib/gzip v1.0.1
github.com/gin-contrib/sessions v1.0.1
github.com/gin-gonic/gin v1.10.0
@ -57,12 +57,12 @@ require (
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06
github.com/sashabaranov/go-openai v1.29.1
github.com/shirou/gopsutil/v3 v3.24.5
github.com/siyuan-note/dejavu v0.0.0-20241206093814-2bd2e504b64f
github.com/siyuan-note/dejavu v0.0.0-20241212013736-f1a4428c07cc
github.com/siyuan-note/encryption v0.0.0-20231219001248-1e028a4d13b4
github.com/siyuan-note/eventbus v0.0.0-20240627125516-396fdb0f0f97
github.com/siyuan-note/filelock v0.0.0-20241202160444-1a99015900ff
github.com/siyuan-note/httpclient v0.0.0-20241203001628-e7e7cab1f949
github.com/siyuan-note/logging v0.0.0-20240505035402-6430d57006a2
github.com/siyuan-note/filelock v0.0.0-20241212013445-c66518cdacfa
github.com/siyuan-note/httpclient v0.0.0-20241212013326-2b23123573c3
github.com/siyuan-note/logging v0.0.0-20241212013108-623e0bb0bcd9
github.com/siyuan-note/riff v0.0.0-20241203002117-3b8d28360e46
github.com/spf13/cast v1.7.0
github.com/steambap/captcha v1.4.1
@ -74,8 +74,8 @@ require (
golang.org/x/image v0.21.0
golang.org/x/mobile v0.0.0-20240520174638-fa72addaaa1b
golang.org/x/mod v0.22.0
golang.org/x/net v0.31.0
golang.org/x/text v0.20.0
golang.org/x/net v0.32.0
golang.org/x/text v0.21.0
golang.org/x/time v0.6.0
gopkg.in/yaml.v3 v3.0.1
)
@ -110,7 +110,7 @@ require (
github.com/go-resty/resty/v2 v2.14.0 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/google/pprof v0.0.0-20241128161848-dc51965c6481 // indirect
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gopherjs/gopherjs v1.17.2 // indirect
github.com/gorilla/context v1.1.2 // indirect
@ -168,11 +168,11 @@ require (
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.uber.org/mock v0.5.0 // indirect
golang.org/x/arch v0.10.0 // indirect
golang.org/x/crypto v0.29.0 // indirect
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect
golang.org/x/sync v0.9.0 // indirect
golang.org/x/sys v0.27.0 // indirect
golang.org/x/tools v0.27.0 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/exp v0.0.0-20241210194714-1829a127f884 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/tools v0.28.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)

View file

@ -12,10 +12,10 @@ github.com/88250/go-humanize v0.0.0-20240424102817-4f78fac47ea7 h1:MafIFwSS0x6A4
github.com/88250/go-humanize v0.0.0-20240424102817-4f78fac47ea7/go.mod h1:HrKCCTin3YNDSLBD02K0AOljjV6eNwc3/zyEI+xyV1I=
github.com/88250/go-sqlite3 v1.14.13-0.20231214121541-e7f54c482950 h1:Pa5hMiBceTVVqrYaDlLio2QSKbXMUmAZPbzCwT5eNCw=
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.20241208103455-3223b6b5f502 h1:agB9kKKmVrmMa4iSpI7YAN4O3OyV3W+w09CTwgnY7Z8=
github.com/88250/lute v1.7.7-0.20241208103455-3223b6b5f502/go.mod h1:VDAzL8b+oCh+e3NAlmwwLzC53ten0rZlS8NboB7ljtk=
github.com/88250/gulu v1.2.3-0.20241212012748-c4dc08fe45ec h1:YsUSpByWJP+x8C+IT+C3QlFvU7ZQ6+E95SFd9+zy6QU=
github.com/88250/gulu v1.2.3-0.20241212012748-c4dc08fe45ec/go.mod h1:c8uVw25vW2W4dhJ/j4iYsX5H1hc19spim266jO5x2hU=
github.com/88250/lute v1.7.7-0.20241213121436-8647e479e280 h1:Ub4MpkyCTOvf3L1po5FUSJ1h52qlrThN8YQtbxbUu2s=
github.com/88250/lute v1.7.7-0.20241213121436-8647e479e280/go.mod h1:WYyUw//5yVw9BJnoVjx7rI/3szsISxNZCYGOqTIrV0o=
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=
@ -112,8 +112,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4=
github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4=
github.com/getsentry/sentry-go v0.29.1 h1:DyZuChN8Hz3ARxGVV8ePaNXh1dQ7d76AiB117xcREwA=
github.com/getsentry/sentry-go v0.29.1/go.mod h1:x3AtIzN01d6SiWkderzaH28Tm0lgkafpJ5Bm3li39O0=
github.com/getsentry/sentry-go v0.30.0 h1:lWUwDnY7sKHaVIoZ9wYqRHJ5iEmoc0pqcRqFkosKzBo=
github.com/getsentry/sentry-go v0.30.0/go.mod h1:WU9B9/1/sHDqeV8T+3VwwbjeR5MSXs/6aqG3mqZrezA=
github.com/gigawattio/window v0.0.0-20180317192513-0f5467e35573 h1:u8AQ9bPa9oC+8/A/jlWouakhIvkFfuxgIIRjiy8av7I=
github.com/gigawattio/window v0.0.0-20180317192513-0f5467e35573/go.mod h1:eBvb3i++NHDH4Ugo9qCvMw8t0mTSctaEa5blJbWcNxs=
github.com/gin-contrib/gzip v1.0.1 h1:HQ8ENHODeLY7a4g1Au/46Z92bdGFl74OhxcZble9WJE=
@ -166,8 +166,8 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20241128161848-dc51965c6481 h1:yudKIrXagAOl99WQzrP1gbz5HLB9UjhcOFnPzdd6Qec=
github.com/google/pprof v0.0.0-20241128161848-dc51965c6481/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg=
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
@ -341,18 +341,18 @@ github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+D
github.com/shurcooL/gofontwoff v0.0.0-20181114050219-180f79e6909d h1:lvCTyBbr36+tqMccdGMwuEU+hjux/zL6xSmf5S9ITaA=
github.com/shurcooL/gofontwoff v0.0.0-20181114050219-180f79e6909d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
github.com/simplereach/timeutils v1.2.0/go.mod h1:VVbQDfN/FHRZa1LSqcwo4kNZ62OOyqLLGQKYB3pB0Q8=
github.com/siyuan-note/dejavu v0.0.0-20241206093814-2bd2e504b64f h1:4NOFYagREPvdf24wG8rMd8qMW0BVnbArIHq4Js6XeyA=
github.com/siyuan-note/dejavu v0.0.0-20241206093814-2bd2e504b64f/go.mod h1:Xu51gaOy8gup2N1q9xwdYdHDqKdCMLL7GiGEno3VnMQ=
github.com/siyuan-note/dejavu v0.0.0-20241212013736-f1a4428c07cc h1:QT2xfpSFChSdvw7uzY9S46pnKtsFkWKS0OOFfyjOtmA=
github.com/siyuan-note/dejavu v0.0.0-20241212013736-f1a4428c07cc/go.mod h1:ytDHT/FQKl1SuGEsnI2YQbhirlswQiiwc0rSW3nnnFo=
github.com/siyuan-note/encryption v0.0.0-20231219001248-1e028a4d13b4 h1:kJaw5L/evyW6LcB9IQT8PR4ppx8JVqOFP9Ix3rfwSrc=
github.com/siyuan-note/encryption v0.0.0-20231219001248-1e028a4d13b4/go.mod h1:UYcCCY+0wh+GmUoDOaO63j1sV5lgy7laLAk1XhEiUis=
github.com/siyuan-note/eventbus v0.0.0-20240627125516-396fdb0f0f97 h1:lM5v8BfNtbOL5jYwhCdMYBcYtr06IYBKjjSLAPMKTM8=
github.com/siyuan-note/eventbus v0.0.0-20240627125516-396fdb0f0f97/go.mod h1:1/nGgthl89FPA7GzAcEWKl6zRRnfgyTjzLZj9bW7kuw=
github.com/siyuan-note/filelock v0.0.0-20241202160444-1a99015900ff h1:Ec6Q5dI/q2uSiFxSkbHajLQgqQQ3mFYXwZKiOSldmE4=
github.com/siyuan-note/filelock v0.0.0-20241202160444-1a99015900ff/go.mod h1:QUxfb/zE/lMrpiGBV9MBT5c5NKQanGvpdBXtMwWjTD0=
github.com/siyuan-note/httpclient v0.0.0-20241203001628-e7e7cab1f949 h1:6tiXciMclIbCkgpbShVEh3LhUnz3YgfxPykdoHWAdIs=
github.com/siyuan-note/httpclient v0.0.0-20241203001628-e7e7cab1f949/go.mod h1:vQ9SXdb5eqqE0AxULHuW+pvHfWBILEUqV1Xx3zF4WM4=
github.com/siyuan-note/logging v0.0.0-20240505035402-6430d57006a2 h1:/2+tlOThVB86RxSLeW0JFw2ISUrH2ZFRg15ULGAUGAE=
github.com/siyuan-note/logging v0.0.0-20240505035402-6430d57006a2/go.mod h1:3Osd2/nwzXZFl6ZcDE4hA0HD83Wyv1fds47nVuapyOM=
github.com/siyuan-note/filelock v0.0.0-20241212013445-c66518cdacfa h1:NM/0m8/hmFZGo0v+xNnjEeoqTtZShcVadWS0WYah+yI=
github.com/siyuan-note/filelock v0.0.0-20241212013445-c66518cdacfa/go.mod h1:OmJuq0Dm+S8I713lmfmZNFwtQ/o4mdZHSfsEra39lfY=
github.com/siyuan-note/httpclient v0.0.0-20241212013326-2b23123573c3 h1:9iSRzXsEmDgofy11kjtiWFC83wqHsGSHZ0meW0qiCJw=
github.com/siyuan-note/httpclient v0.0.0-20241212013326-2b23123573c3/go.mod h1:PJNk4sdv+CQFjlAFJhv821asNZhJmBoa1Jbe4TAGQlg=
github.com/siyuan-note/logging v0.0.0-20241212013108-623e0bb0bcd9 h1:DYF4JZFiVTy1hmFvrpyRxc+8qaymm/THwdyow/xWark=
github.com/siyuan-note/logging v0.0.0-20241212013108-623e0bb0bcd9/go.mod h1:jZ4dO1KQBl3mqarQCwL8bnAyWz3SeZsySLT6HjSuZjY=
github.com/siyuan-note/riff v0.0.0-20241203002117-3b8d28360e46 h1:HHplIFTgsRdPJo3uQuVgzqwiw2+Uyurkpee8NAO+k+Q=
github.com/siyuan-note/riff v0.0.0-20241203002117-3b8d28360e46/go.mod h1:/mdhL34JKGnWtVuhenkIfeqqBeZTZg6ZOIklMXA7pvA=
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
@ -425,10 +425,10 @@ golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo=
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/exp v0.0.0-20241210194714-1829a127f884 h1:Y/Mj/94zIQQGHVSv1tTtQBDaQaJe62U9bkDZKKyhPCU=
golang.org/x/exp v0.0.0-20241210194714-1829a127f884/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s=
golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78=
@ -456,16 +456,16 @@ golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -486,8 +486,8 @@ golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@ -515,8 +515,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -525,8 +525,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o=
golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q=
golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=

View file

@ -19,6 +19,7 @@ package model
import (
"bytes"
"fmt"
"math/rand"
"os"
"path/filepath"
"slices"
@ -3112,7 +3113,11 @@ func UpdateAttributeViewCell(tx *Transaction, avID, keyID, rowID string, valueDa
for _, valOpt := range val.MSelect {
if opt := key.GetOption(valOpt.Content); nil == opt {
// 不存在的选项新建保存
opt = &av.SelectOption{Name: valOpt.Content, Color: valOpt.Color}
color := valOpt.Color
if "" == color {
color = fmt.Sprintf("%d", 1+rand.Intn(14))
}
opt = &av.SelectOption{Name: valOpt.Content, Color: color}
key.Options = append(key.Options, opt)
} else {
// 已经存在的选项颜色需要保持不变

View file

@ -62,8 +62,7 @@ type Backlink struct {
node *ast.Node // 仅用于按文档内容顺序排序
}
func GetBackmentionDoc(defID, refTreeID, keyword string, containChildren, highlight bool) (ret []*Backlink) {
var keywords []string
func GetBackmentionDoc(defID, refTreeID, keyword string, containChildren, highlight bool) (ret []*Backlink, keywords []string) {
keyword = strings.TrimSpace(keyword)
if "" != keyword {
keywords = strings.Split(keyword, " ")
@ -98,6 +97,11 @@ func GetBackmentionDoc(defID, refTreeID, keyword string, containChildren, highli
mentionKeywords = append(mentionKeywords, strings.Split(keyword, " ")...)
}
mentionKeywords = gulu.Str.RemoveDuplicatedElem(mentionKeywords)
keywords = append(keywords, mentionKeywords...)
keywords = gulu.Str.RemoveDuplicatedElem(keywords)
if 1 > len(keywords) {
keywords = []string{}
}
var refTree *parse.Tree
trees := filesys.LoadTrees(mentionBlockIDs)
@ -118,12 +122,15 @@ func GetBackmentionDoc(defID, refTreeID, keyword string, containChildren, highli
return
}
func GetBacklinkDoc(defID, refTreeID, keyword string, containChildren, highlight bool) (ret []*Backlink) {
var keywords []string
func GetBacklinkDoc(defID, refTreeID, keyword string, containChildren, highlight bool) (ret []*Backlink, keywords []string) {
keyword = strings.TrimSpace(keyword)
if "" != keyword {
keywords = strings.Split(keyword, " ")
}
keywords = gulu.Str.RemoveDuplicatedElem(keywords)
if 1 > len(keywords) {
keywords = []string{}
}
ret = []*Backlink{}
sqlBlock := sql.GetBlock(defID)

View file

@ -547,6 +547,22 @@ func GetHeadingChildrenDOM(id string) (ret string) {
nodes := append([]*ast.Node{}, heading)
children := treenode.HeadingChildren(heading)
nodes = append(nodes, children...)
// 取消折叠 https://github.com/siyuan-note/siyuan/issues/13232#issuecomment-2535955152
for _, child := range children {
ast.Walk(child, func(n *ast.Node, entering bool) ast.WalkStatus {
if !entering {
return ast.WalkContinue
}
n.RemoveIALAttr("heading-fold")
n.RemoveIALAttr("fold")
return ast.WalkContinue
})
}
heading.RemoveIALAttr("fold")
heading.RemoveIALAttr("heading-fold")
luteEngine := util.NewLute()
ret = renderBlockDOMByNodes(nodes, luteEngine)
return

View file

@ -215,6 +215,22 @@ func setNodeAttrs0(node *ast.Node, nameValues map[string]string) (oldAttrs map[s
}
}
if tag, ok := nameValues["tags"]; ok {
var tags []string
tmp := strings.Split(tag, ",")
for _, t := range tmp {
t = util.RemoveInvalid(t)
t = strings.TrimSpace(t)
if "" != t {
tags = append(tags, t)
}
}
tags = gulu.Str.RemoveDuplicatedElem(tags)
if 0 < len(tags) {
nameValues["tags"] = strings.Join(tags, ",")
}
}
for name, value := range nameValues {
value = util.RemoveInvalid(value)
value = strings.TrimSpace(value)

View file

@ -344,6 +344,12 @@ func InitConf() {
if 0 == Conf.Sync.Mode {
Conf.Sync.Mode = 1
}
if 30 > Conf.Sync.Interval {
Conf.Sync.Interval = 30
}
if 60*60*12 < Conf.Sync.Interval {
Conf.Sync.Interval = 60 * 60 * 12
}
if nil == Conf.Sync.S3 {
Conf.Sync.S3 = &conf.S3{PathStyle: true, SkipTlsVerify: true}
}

View file

@ -532,7 +532,13 @@ func ExportResources(resourcePaths []string, mainName string) (exportFilePath st
// 将需要导出的文件/文件夹复制到临时文件夹
for _, resourcePath := range resourcePaths {
resourceFullPath := filepath.Join(util.WorkspaceDir, resourcePath) // 资源完整路径
resourceFullPath := filepath.Join(util.WorkspaceDir, resourcePath) // 资源完整路径
if !util.IsAbsPathInWorkspace(resourceFullPath) {
logging.LogErrorf("resource path [%s] is not in workspace", resourceFullPath)
err = errors.New("resource path [" + resourcePath + "] is not in workspace")
return
}
resourceBaseName := filepath.Base(resourceFullPath) // 资源名称
resourceCopyPath := filepath.Join(exportFolderPath, resourceBaseName) // 资源副本完整路径
if err = filelock.Copy(resourceFullPath, resourceCopyPath); err != nil {
@ -571,7 +577,7 @@ func Preview(id string) (retStdHTML string) {
blockRefMode, Conf.Export.BlockEmbedMode, Conf.Export.FileAnnotationRefMode,
Conf.Export.TagOpenMarker, Conf.Export.TagCloseMarker,
Conf.Export.BlockRefTextLeft, Conf.Export.BlockRefTextRight,
Conf.Export.AddTitle)
Conf.Export.AddTitle, true)
luteEngine := NewLute()
luteEngine.SetFootnotes(true)
addBlockIALNodes(tree, false)
@ -674,7 +680,7 @@ func ExportMarkdownHTML(id, savePath string, docx, merge bool) (name, dom string
blockRefMode, Conf.Export.BlockEmbedMode, Conf.Export.FileAnnotationRefMode,
Conf.Export.TagOpenMarker, Conf.Export.TagCloseMarker,
Conf.Export.BlockRefTextLeft, Conf.Export.BlockRefTextRight,
Conf.Export.AddTitle)
Conf.Export.AddTitle, true)
name = path.Base(tree.HPath)
name = util.FilterFileName(name) // 导出 PDF、HTML 和 Word 时未移除不支持的文件名符号 https://github.com/siyuan-note/siyuan/issues/5614
savePath = strings.TrimSpace(savePath)
@ -833,7 +839,7 @@ func ExportHTML(id, savePath string, pdf, image, keepFold, merge bool) (name, do
blockRefMode, Conf.Export.BlockEmbedMode, Conf.Export.FileAnnotationRefMode,
Conf.Export.TagOpenMarker, Conf.Export.TagCloseMarker,
Conf.Export.BlockRefTextLeft, Conf.Export.BlockRefTextRight,
Conf.Export.AddTitle)
Conf.Export.AddTitle, true)
name = path.Base(tree.HPath)
name = util.FilterFileName(name) // 导出 PDF、HTML 和 Word 时未移除不支持的文件名符号 https://github.com/siyuan-note/siyuan/issues/5614
@ -1967,7 +1973,7 @@ func exportMarkdownContent0(tree *parse.Tree, cloudAssetsBase string, assetsDest
blockRefMode, blockEmbedMode, fileAnnotationRefMode,
tagOpenMarker, tagCloseMarker,
blockRefTextLeft, blockRefTextRight,
addTitle)
addTitle, 0 < len(defBlockIDs))
luteEngine := NewLute()
luteEngine.SetFootnotes(true)
luteEngine.SetKramdownIAL(false)
@ -2078,7 +2084,7 @@ func exportTree(tree *parse.Tree, wysiwyg, keepFold, avHiddenCol bool,
blockRefMode, blockEmbedMode, fileAnnotationRefMode int,
tagOpenMarker, tagCloseMarker string,
blockRefTextLeft, blockRefTextRight string,
addTitle bool) (ret *parse.Tree) {
addTitle, addDocAnchorSpan bool) (ret *parse.Tree) {
luteEngine := NewLute()
ret = tree
id := tree.Root.ID

View file

@ -17,7 +17,6 @@
package model
import (
"bytes"
"errors"
"fmt"
"io/fs"
@ -442,163 +441,6 @@ func ListDocTree(boxID, listPath string, sortMode int, flashcard, showHidden boo
return
}
func ContentStat(content string) (ret *util.BlockStatResult) {
luteEngine := util.NewLute()
return contentStat(content, luteEngine)
}
func contentStat(content string, luteEngine *lute.Lute) (ret *util.BlockStatResult) {
tree := luteEngine.BlockDOM2Tree(content)
runeCnt, wordCnt, linkCnt, imgCnt, refCnt := tree.Root.Stat()
return &util.BlockStatResult{
RuneCount: runeCnt,
WordCount: wordCnt,
LinkCount: linkCnt,
ImageCount: imgCnt,
RefCount: refCnt,
}
}
func BlocksWordCount(ids []string) (ret *util.BlockStatResult) {
ret = &util.BlockStatResult{}
trees := filesys.LoadTrees(ids)
for _, id := range ids {
tree := trees[id]
if nil == tree {
continue
}
node := treenode.GetNodeInTree(tree, id)
if nil == node {
continue
}
runeCnt, wordCnt, linkCnt, imgCnt, refCnt := node.Stat()
ret.RuneCount += runeCnt
ret.WordCount += wordCnt
ret.LinkCount += linkCnt
ret.ImageCount += imgCnt
ret.RefCount += refCnt
}
ret.BlockCount = len(ids)
return
}
func StatTree(id string) (ret *util.BlockStatResult) {
FlushTxQueue()
tree, _ := LoadTreeByBlockID(id)
if nil == tree {
return
}
blockCount := 0
var databaseBlockNodes []*ast.Node
ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
if !entering {
return ast.WalkContinue
}
if n.IsBlock() {
blockCount++
}
if ast.NodeAttributeView != n.Type {
return ast.WalkContinue
}
databaseBlockNodes = append(databaseBlockNodes, n)
return ast.WalkContinue
})
luteEngine := util.NewLute()
var dbRuneCnt, dbWordCnt, dbLinkCnt, dbImgCnt, dbRefCnt int
for _, n := range databaseBlockNodes {
if "" == n.AttributeViewID {
continue
}
attrView, _ := av.ParseAttributeView(n.AttributeViewID)
if nil == attrView {
continue
}
content := bytes.Buffer{}
for _, kValues := range attrView.KeyValues {
for _, v := range kValues.Values {
switch kValues.Key.Type {
case av.KeyTypeURL:
if v.IsEmpty() {
continue
}
dbLinkCnt++
content.WriteString(v.URL.Content)
case av.KeyTypeMAsset:
if v.IsEmpty() {
continue
}
for _, asset := range v.MAsset {
if av.AssetTypeImage == asset.Type {
dbImgCnt++
}
}
case av.KeyTypeBlock:
if v.IsEmpty() {
continue
}
if !v.IsDetached {
dbRefCnt++
}
content.WriteString(v.Block.Content)
case av.KeyTypeText:
if v.IsEmpty() {
continue
}
content.WriteString(v.Text.Content)
case av.KeyTypeNumber:
if v.IsEmpty() {
continue
}
v.Number.FormatNumber()
content.WriteString(v.Number.FormattedContent)
case av.KeyTypeEmail:
if v.IsEmpty() {
continue
}
content.WriteString(v.Email.Content)
case av.KeyTypePhone:
if v.IsEmpty() {
continue
}
content.WriteString(v.Phone.Content)
}
}
}
dbStat := contentStat(content.String(), luteEngine)
dbRuneCnt += dbStat.RuneCount
dbWordCnt += dbStat.WordCount
}
runeCnt, wordCnt, linkCnt, imgCnt, refCnt := tree.Root.Stat()
runeCnt += dbRuneCnt
wordCnt += dbWordCnt
linkCnt += dbLinkCnt
imgCnt += dbImgCnt
refCnt += dbRefCnt
return &util.BlockStatResult{
RuneCount: runeCnt,
WordCount: wordCnt,
LinkCount: linkCnt,
ImageCount: imgCnt,
RefCount: refCnt,
BlockCount: blockCount,
}
}
func GetDoc(startID, endID, id string, index int, query string, queryTypes map[string]bool, queryMethod, mode int, size int, isBacklink, highlight bool) (
blockCount int, dom, parentID, parent2ID, rootID, typ string, eof, scroll bool, boxID, docPath string, isBacklinkExpand bool, keywords []string, err error) {
//os.MkdirAll("pprof", 0755)
@ -795,13 +637,13 @@ func GetDoc(startID, endID, id string, index int, query string, queryTypes map[s
query = filterQueryInvisibleChars(query)
if "" != query && (0 == queryMethod || 1 == queryMethod || 3 == queryMethod) { // 只有关键字、查询语法和正则表达式搜索支持高亮
if 0 == queryMethod {
query = stringQuery(query)
}
typeFilter := buildTypeFilter(queryTypes)
if 0 == queryMethod || 1 == queryMethod {
switch queryMethod {
case 0:
keywords = strings.Split(query, " ")
case 1:
keywords = highlightByFTS(query, typeFilter, rootID)
} else {
case 3:
keywords = highlightByRegexp(query, typeFilter, rootID)
}
}
@ -1077,21 +919,16 @@ func writeTreeUpsertQueue(tree *parse.Tree) (err error) {
return
}
func writeTreeIndexQueue(tree *parse.Tree) (err error) {
size, err := filesys.WriteTree(tree)
func indexWriteTreeIndexQueue(tree *parse.Tree) (err error) {
treenode.IndexBlockTree(tree)
_, err = filesys.WriteTree(tree)
if err != nil {
return
}
sql.IndexTreeQueue(tree)
refreshDocInfo(tree, size)
return
}
func indexWriteTreeIndexQueue(tree *parse.Tree) (err error) {
treenode.IndexBlockTree(tree)
return writeTreeIndexQueue(tree)
}
func indexWriteTreeUpsertQueue(tree *parse.Tree) (err error) {
treenode.UpsertBlockTree(tree)
return writeTreeUpsertQueue(tree)
@ -2226,3 +2063,36 @@ func (box *Box) addSort(previousPath, id string) {
return
}
}
func (box *Box) setSort(sortIDVals map[string]int) {
confPath := filepath.Join(util.DataDir, box.ID, ".siyuan", "sort.json")
if !filelock.IsExist(confPath) {
return
}
data, err := filelock.ReadFile(confPath)
if err != nil {
logging.LogErrorf("read sort conf failed: %s", err)
return
}
fullSortIDs := map[string]int{}
if err = gulu.JSON.UnmarshalJSON(data, &fullSortIDs); err != nil {
logging.LogErrorf("unmarshal sort conf failed: %s", err)
return
}
for sortID := range sortIDVals {
fullSortIDs[sortID] = sortIDVals[sortID]
}
data, err = gulu.JSON.MarshalJSON(fullSortIDs)
if err != nil {
logging.LogErrorf("marshal sort conf failed: %s", err)
return
}
if err = filelock.WriteFile(confPath, data); err != nil {
logging.LogErrorf("write sort conf failed: %s", err)
return
}
}

Some files were not shown because too many files have changed in this diff Show more