Compare commits

...

42 commits

Author SHA1 Message Date
Daniel
7acf6f5225
Database kanban view https://github.com/siyuan-note/siyuan/issues/8873
Signed-off-by: Daniel <845765@qq.com>
2025-09-14 22:42:28 +08:00
Daniel
3211d331fa
Database kanban view https://github.com/siyuan-note/siyuan/issues/8873
Signed-off-by: Daniel <845765@qq.com>
2025-09-14 22:17:55 +08:00
Daniel
2d8f4a3030
Database kanban view https://github.com/siyuan-note/siyuan/issues/8873
Signed-off-by: Daniel <845765@qq.com>
2025-09-14 21:55:00 +08:00
Daniel
b8538772f8
🎨 Improve Add to Database
Signed-off-by: Daniel <845765@qq.com>
2025-09-14 21:40:19 +08:00
Daniel
0084a6427b
🐛 Add to Database shows databases that are not in the document https://github.com/siyuan-note/siyuan/issues/15847
Signed-off-by: Daniel <845765@qq.com>
2025-09-14 21:25:06 +08:00
Vanessa
1b077c164c 💄 https://github.com/siyuan-note/siyuan/issues/15837 2025-09-14 21:07:44 +08:00
Daniel
ec77e2bafd
Database kanban view https://github.com/siyuan-note/siyuan/issues/8873
Signed-off-by: Daniel <845765@qq.com>
2025-09-14 12:52:11 +08:00
Vanessa
f8d1d1958b 🎨 https://github.com/siyuan-note/siyuan/issues/15835 2025-09-14 12:38:40 +08:00
Vanessa
327c3aaabb 🎨 https://github.com/siyuan-note/siyuan/issues/15835 2025-09-13 23:27:45 +08:00
Vanessa
c150c16273 Merge remote-tracking branch 'origin/dev' into dev 2025-09-13 12:25:27 +08:00
Vanessa
9fff044869 🎨 https://github.com/siyuan-note/siyuan/issues/15839 2025-09-13 12:25:13 +08:00
Daniel
be6e92ad7a
🐛 Optimize typography exception when code block contains ``` https://github.com/siyuan-note/siyuan/issues/15843
Signed-off-by: Daniel <845765@qq.com>
2025-09-13 11:59:08 +08:00
Vanessa
bcc0472ed0 🎨 https://github.com/siyuan-note/siyuan/issues/15839 2025-09-13 11:50:13 +08:00
Daniel
7faf981b69
🎨 When "Default fill created time" is enabled for database date fields, the automatically filled time value is incorrect https://github.com/siyuan-note/siyuan/issues/15828
Signed-off-by: Daniel <845765@qq.com>
2025-09-13 09:45:28 +08:00
Vanessa
370601d277 Merge remote-tracking branch 'origin/dev' into dev 2025-09-13 09:29:29 +08:00
Vanessa
a99a55b512 🎨 https://github.com/siyuan-note/siyuan/issues/15840 2025-09-13 09:29:17 +08:00
Daniel
ba0c6883e6
🐛 File history cannot load the correct view of database blocks https://github.com/siyuan-note/siyuan/issues/15841
Signed-off-by: Daniel <845765@qq.com>
2025-09-13 09:21:06 +08:00
Vanessa
41f72e65d3 🚨 2025-09-13 09:15:54 +08:00
Vanessa
b70328c7e7 🎨 https://github.com/siyuan-note/siyuan/issues/15821 2025-09-13 09:15:06 +08:00
Vanessa
2ff8c148bd 💄 https://github.com/siyuan-note/siyuan/issues/15837 2025-09-13 08:59:41 +08:00
Vanessa
5797872b97 💄 https://github.com/siyuan-note/siyuan/issues/15842 2025-09-13 08:45:16 +08:00
Daniel
9d569ad37b
🎨 Support arm64 version in Microsoft Store https://github.com/siyuan-note/siyuan/issues/15836
Signed-off-by: Daniel <845765@qq.com>
2025-09-12 22:30:36 +08:00
Daniel
eca318c4ff
🎨 https://github.com/siyuan-note/siyuan/issues/15833 point 2
Signed-off-by: Daniel <845765@qq.com>
2025-09-12 19:49:09 +08:00
QYLexpired
9f76274747
为面包屑解锁/锁定按钮增加属性 (#15820)
* Update index.ts

* Update onGet.ts

* Update onGet.ts
2025-09-12 19:48:25 +08:00
Vanessa
582f60f574 🐛 https://github.com/siyuan-note/siyuan/issues/15827 2025-09-12 19:42:21 +08:00
Vanessa
a2329077e7 Merge remote-tracking branch 'origin/dev' into dev 2025-09-12 19:25:53 +08:00
Vanessa
a63686cb05 🎨 https://github.com/siyuan-note/siyuan/issues/15833 2025-09-12 19:25:38 +08:00
Daniel
d6e7d0163a
Database kanban view https://github.com/siyuan-note/siyuan/issues/8873
Signed-off-by: Daniel <845765@qq.com>
2025-09-12 19:23:09 +08:00
Daniel
6cc6ef66f9
🎨 Improve database date fields to automatically fill in creation time https://github.com/siyuan-note/siyuan/issues/15828
🎨 Improve database date fields to automatically fill in creation time https://github.com/siyuan-note/siyuan/issues/15828
2025-09-12 19:23:09 +08:00
Vanessa
72c84f5f3d 🎨 https://github.com/siyuan-note/siyuan/issues/15831 2025-09-12 17:13:30 +08:00
Vanessa
043511c2b8 🎨 https://github.com/siyuan-note/siyuan/issues/15831 2025-09-12 12:33:07 +08:00
Vanessa
e37ab0db78 🎨 https://github.com/siyuan-note/siyuan/issues/15825 2025-09-12 12:16:44 +08:00
Vanessa
fd83286438 🎨 https://github.com/siyuan-note/siyuan/issues/15821 2025-09-11 22:44:32 +08:00
Vanessa
24285aae56 🎨 https://github.com/siyuan-note/siyuan/issues/15821 2025-09-11 22:27:51 +08:00
Vanessa
8cbefc8788 Merge remote-tracking branch 'origin/dev' into dev 2025-09-11 22:25:18 +08:00
Vanessa
deb9b933af 🎨 https://github.com/siyuan-note/siyuan/issues/15762 2025-09-11 22:25:02 +08:00
Daniel
8476ddd18c
🎨 Block ref elements retain their original styles when exported https://github.com/siyuan-note/siyuan/issues/15698
Signed-off-by: Daniel <845765@qq.com>
2025-09-11 17:54:25 +08:00
Daniel
ee4ddf89c1
🎨 Refresh the data in the interface after moving the document https://github.com/siyuan-note/siyuan/issues/15762
Signed-off-by: Daniel <845765@qq.com>
2025-09-10 21:39:14 +08:00
Vanessa
d5e7b27a11 Merge remote-tracking branch 'origin/dev' into dev 2025-09-10 17:54:03 +08:00
Vanessa
9c37468386 🎨 https://github.com/siyuan-note/siyuan/issues/15762 2025-09-10 17:53:51 +08:00
Daniel
de19d69f99
Improve database group view performance https://github.com/siyuan-note/siyuan/issues/15811
Signed-off-by: Daniel <845765@qq.com>
2025-09-10 16:50:11 +08:00
Vanessa
9a15b466f3 🎨 https://github.com/siyuan-note/siyuan/issues/15819 2025-09-10 16:47:12 +08:00
69 changed files with 1742 additions and 745 deletions

View file

@ -1,5 +1,9 @@
document.body.insertAdjacentHTML('afterbegin', `<svg id="iconsAnt" style="position: absolute; width: 0; height: 0; overflow: hidden;" xmlns="http://www.w3.org/2000/svg">
<defs>
<symbol id="iconInclude" viewBox="0 0 32 32">
<path d="M22.091 2.435v7.747h7.582v13.65h-2.637v-11.013h-4.945v9.156h-9.436v5.11h11.036v2.637h-13.674v-7.747h-7.582v-13.65h2.637v11.013h4.945v-9.156h9.436l0-5.11h-11.036v-2.637h13.674zM19.454 12.82h-6.799v6.518l6.799 0v-6.518z"></path>
<path d="M1 1h5.604v5.604h-5.604zM25.396 1h5.604v5.604h-5.604zM25.396 25.396h5.604v5.604h-5.604zM1 25.396h5.604v5.604h-5.604z"></path>
</symbol>
<symbol id="iconGroups" viewBox="0 0 32 32">
<path d="M3.074 1c-0.646 0-1.17 0.524-1.17 1.17s0.524 1.17 1.17 1.17h25.851c0.646 0 1.17-0.524 1.17-1.17s-0.524-1.17-1.17-1.17v0zM3.074 17.526c-0.646 0-1.17 0.524-1.17 1.17s0.524 1.17 1.17 1.17h25.851c0.646 0 1.17-0.524 1.17-1.17s-0.524-1.17-1.17-1.17v0zM1.904 8.517c0-1.175 0.953-2.128 2.128-2.128v0h3.83c1.175 0 2.128 0.953 2.128 2.128v0 3.83c0 1.175-0.953 2.128-2.128 2.128v0h-3.83c-1.175 0-2.128-0.953-2.128-2.128v0zM4.245 8.73v3.404h3.404v-3.404zM14.085 6.389c-1.175 0-2.128 0.953-2.128 2.128v0 3.83c0 1.175 0.953 2.128 2.128 2.128v0h3.83c1.175 0 2.128-0.953 2.128-2.128v0-3.83c0-1.175-0.953-2.128-2.128-2.128v0zM14.298 12.134v-3.404h3.404v3.404zM22.011 8.517c0-1.175 0.953-2.128 2.128-2.128v0h3.83c1.175 0 2.128 0.953 2.128 2.128v0 3.83c0 1.175-0.953 2.128-2.128 2.128v0h-3.83c-1.175 0-2.128-0.953-2.128-2.128v0zM24.351 8.73v3.404h3.404v-3.404zM4.032 22.915c-1.175 0-2.128 0.953-2.128 2.128v0 3.83c0 1.175 0.953 2.128 2.128 2.128v0h3.83c1.175 0 2.128-0.953 2.128-2.128v0-3.83c0-1.175-0.953-2.128-2.128-2.128v0zM4.245 28.66v-3.404h3.404v3.404zM11.957 25.043c0-1.175 0.953-2.128 2.128-2.128v0h3.83c1.175 0 2.128 0.953 2.128 2.128v0 3.83c0 1.175-0.953 2.128-2.128 2.128v0h-3.83c-1.175 0-2.128-0.953-2.128-2.128v0zM14.298 25.255v3.404h3.404v-3.404zM24.138 22.915c-1.175 0-2.128 0.953-2.128 2.128v0 3.83c0 1.175 0.953 2.128 2.128 2.128v0h3.83c1.175 0 2.128-0.953 2.128-2.128v0-3.83c0-1.175-0.953-2.128-2.128-2.128v0zM24.351 28.66v-3.404h3.404v3.404z"></path>
</symbol>

View file

@ -2,5 +2,5 @@
"name": "ant",
"author": "Vanessa",
"url": "https://github.com/Vanessa219",
"version": "1.34.0"
"version": "1.35.0"
}

View file

@ -28,6 +28,12 @@
<body>
<h2>SiYuan</h2>
<div class="fn__clear">
<div>
<svg>
<use xlink:href="#iconInclude"></use>
</svg>
iconGroups
</div>
<div>
<svg>
<use xlink:href="#iconGroups"></use>

View file

@ -1,5 +1,9 @@
document.body.insertAdjacentHTML('afterbegin', `<svg id="iconsMaterial" style="position: absolute; width: 0; height: 0; overflow: hidden;" xmlns="http://www.w3.org/2000/svg">
<defs>
<symbol id="iconInclude" viewBox="0 0 32 32">
<path d="M22.091 2.435v7.747h7.582v13.65h-2.637v-11.013h-4.945v9.156h-9.436v5.11h11.036v2.637h-13.674v-7.747h-7.582v-13.65h2.637v11.013h4.945v-9.156h9.436l0-5.11h-11.036v-2.637h13.674zM19.454 12.82h-6.799v6.518l6.799 0v-6.518z"></path>
<path d="M1 1h5.604v5.604h-5.604zM25.396 1h5.604v5.604h-5.604zM25.396 25.396h5.604v5.604h-5.604zM1 25.396h5.604v5.604h-5.604z"></path>
</symbol>
<symbol id="iconGroups" viewBox="0 0 32 32">
<path d="M3.074 1c-0.646 0-1.17 0.524-1.17 1.17s0.524 1.17 1.17 1.17h25.851c0.646 0 1.17-0.524 1.17-1.17s-0.524-1.17-1.17-1.17v0zM3.074 17.526c-0.646 0-1.17 0.524-1.17 1.17s0.524 1.17 1.17 1.17h25.851c0.646 0 1.17-0.524 1.17-1.17s-0.524-1.17-1.17-1.17v0zM1.904 8.517c0-1.175 0.953-2.128 2.128-2.128v0h3.83c1.175 0 2.128 0.953 2.128 2.128v0 3.83c0 1.175-0.953 2.128-2.128 2.128v0h-3.83c-1.175 0-2.128-0.953-2.128-2.128v0zM4.245 8.73v3.404h3.404v-3.404zM14.085 6.389c-1.175 0-2.128 0.953-2.128 2.128v0 3.83c0 1.175 0.953 2.128 2.128 2.128v0h3.83c1.175 0 2.128-0.953 2.128-2.128v0-3.83c0-1.175-0.953-2.128-2.128-2.128v0zM14.298 12.134v-3.404h3.404v3.404zM22.011 8.517c0-1.175 0.953-2.128 2.128-2.128v0h3.83c1.175 0 2.128 0.953 2.128 2.128v0 3.83c0 1.175-0.953 2.128-2.128 2.128v0h-3.83c-1.175 0-2.128-0.953-2.128-2.128v0zM24.351 8.73v3.404h3.404v-3.404zM4.032 22.915c-1.175 0-2.128 0.953-2.128 2.128v0 3.83c0 1.175 0.953 2.128 2.128 2.128v0h3.83c1.175 0 2.128-0.953 2.128-2.128v0-3.83c0-1.175-0.953-2.128-2.128-2.128v0zM4.245 28.66v-3.404h3.404v3.404zM11.957 25.043c0-1.175 0.953-2.128 2.128-2.128v0h3.83c1.175 0 2.128 0.953 2.128 2.128v0 3.83c0 1.175-0.953 2.128-2.128 2.128v0h-3.83c-1.175 0-2.128-0.953-2.128-2.128v0zM14.298 25.255v3.404h3.404v-3.404zM24.138 22.915c-1.175 0-2.128 0.953-2.128 2.128v0 3.83c0 1.175 0.953 2.128 2.128 2.128v0h3.83c1.175 0 2.128-0.953 2.128-2.128v0-3.83c0-1.175-0.953-2.128-2.128-2.128v0zM24.351 28.66v-3.404h3.404v3.404z"></path>
</symbol>

View file

@ -2,5 +2,5 @@
"name": "material",
"author": "Vanessa",
"url": "https://github.com/Vanessa219",
"version": "1.34.0"
"version": "1.35.0"
}

View file

@ -36,6 +36,7 @@
"showAllEntriesIcons": "عرض أيقونات المدخلات",
"wrapAllFields": "التفاف الحقول تلقائيًا",
"gallery": "بطاقة",
"kanban": "Kanban",
"newTag": "علامة جديدة",
"pleaseWait": "يرجى الانتظار...",
"reconnectPrompt": "بعد تبديل التطبيقات، سيستغرق الأمر بعض الوقت لاستعادة تشغيل نواة SiYuan. يرجى الانتظار بضع ثوانٍ أو النقر فوق الزر \"إعادة المحاولة\"",
@ -1387,6 +1388,7 @@
"_attrView": {
"table": "جدول",
"gallery": "بطاقة",
"kanban": "Kanban",
"key": "المفتاح الرئيسي",
"select": "تحديد"
},

View file

@ -36,6 +36,7 @@
"showAllEntriesIcons": "Eintragssymbole anzeigen",
"wrapAllFields": "Felder automatisch umbrechen",
"gallery": "Karte",
"kanban": "Kanban",
"newTag": "Neuer Tag",
"pleaseWait": "Bitte warten...",
"reconnectPrompt": "Nach dem Wechseln der Anwendungen dauert es einige Zeit, bis der Betrieb des SiYuan-Kernels wiederhergestellt ist. Bitte warten Sie einige Sekunden oder klicken Sie auf die Schaltfläche „Erneut versuchen“",
@ -1387,6 +1388,7 @@
"_attrView": {
"table": "Tabelle",
"gallery": "Karte",
"kanban": "Kanban",
"key": "Primärschlüssel",
"select": "Auswählen"
},

View file

@ -36,6 +36,7 @@
"showAllEntriesIcons": "Show entry icons",
"wrapAllFields": "Auto-wrap fields",
"gallery": "Card",
"kanban": "Kanban",
"newTag": "New tag",
"pleaseWait": "Please wait...",
"reconnectPrompt": "After switching applications, it will take some time to restore the SiYuan kernel operation. Please wait a few seconds or click the \"Retry\" button",
@ -1387,6 +1388,7 @@
"_attrView": {
"table": "Table",
"gallery": "Card",
"kanban": "Kanban",
"key": "Primary Key",
"select": "Select"
},

View file

@ -36,6 +36,7 @@
"showAllEntriesIcons": "Mostrar íconos de entradas",
"wrapAllFields": "Ajuste automático de campos",
"gallery": "Tarjeta",
"kanban": "Kanban",
"newTag": "Nueva etiqueta",
"pleaseWait": "Por favor, espere...",
"reconnectPrompt": "Después de cambiar de aplicación, tomará algún tiempo restaurar el funcionamiento del núcleo de SiYuan. Espere unos segundos o haga clic en el botón \"Reintentar\"",
@ -1387,6 +1388,7 @@
"_attrView": {
"table": "Tabla",
"gallery": "Tarjeta",
"kanban": "Kanban",
"key": "Clave principal",
"select": "Selección"
},

View file

@ -36,6 +36,7 @@
"showAllEntriesIcons": "Afficher les icônes des entrées",
"wrapAllFields": "Retour automatique des champs",
"gallery": "Carte",
"kanban": "Kanban",
"newTag": "Nouvelle étiquette",
"pleaseWait": "Veuillez patienter...",
"reconnectPrompt": "Après avoir changé d'application, il faudra un certain temps pour rétablir le fonctionnement du noyau SiYuan. Veuillez patienter quelques secondes ou cliquer sur le bouton « Réessayer »",
@ -1387,6 +1388,7 @@
"_attrView": {
"table": "Tableau",
"gallery": "Carte",
"kanban": "Kanban",
"key": "Clé primaire",
"select": "Sélectionner"
},

View file

@ -36,6 +36,7 @@
"showAllEntriesIcons": "הצג סמלי כניסות",
"wrapAllFields": "עטיפת שדות אוטומטית",
"gallery": "כרטיס",
"kanban": "קאנבן",
"newTag": "תג חדש",
"pleaseWait": "אנא המתן...",
"reconnectPrompt": "לאחר מעבר בין יישומים, יידרש זמן מה כדי לשחזר את פעולת ליבת SiYuan. אנא המתן מספר שניות או לחץ על כפתור \"נסה שוב\"",
@ -1387,6 +1388,7 @@
"_attrView": {
"table": "טבלה",
"gallery": "כרטיס",
"kanban": "קאנבן",
"key": "מפתח ראשי",
"select": "בחר"
},

View file

@ -36,6 +36,7 @@
"showAllEntriesIcons": "Mostra icone delle voci",
"wrapAllFields": "Avvolgi automaticamente i campi",
"gallery": "Scheda",
"kanban": "Kanban",
"newTag": "Nuova etichetta",
"pleaseWait": "Attendere prego...",
"reconnectPrompt": "Dopo aver cambiato applicazione, ci vorrà un po' di tempo per ripristinare il funzionamento del kernel SiYuan. Attendere qualche secondo o fare clic sul pulsante \"Riprova\"",
@ -1387,6 +1388,7 @@
"_attrView": {
"table": "Tabella",
"gallery": "Scheda",
"kanban": "Kanban",
"key": "Chiave primaria",
"select": "Seleziona"
},

View file

@ -36,6 +36,7 @@
"showAllEntriesIcons": "エントリアイコンを表示",
"wrapAllFields": "フィールドを自動折り返し",
"gallery": "カード",
"kanban": "カンバン",
"newTag": "新しいタグ",
"pleaseWait": "しばらくお待ちください...",
"reconnectPrompt": "アプリを切り替えた後、思源カーネルの実行を再開するには少し時間がかかります。数秒待つか、「再試行」ボタンをクリックしてください",
@ -1387,6 +1388,7 @@
"_attrView": {
"table": "テーブル",
"gallery": "カード",
"kanban": "カンバン",
"key": "プライマリキー",
"select": "選択"
},

View file

@ -36,6 +36,7 @@
"showAllEntriesIcons": "Pokaż ikony wpisów",
"wrapAllFields": "Automatyczne zawijanie pól",
"gallery": "Karta",
"kanban": "Kanban",
"newTag": "Nowy tag",
"pleaseWait": "Proszę czekać...",
"reconnectPrompt": "Po przełączeniu aplikacji ponowne uruchomienie jądra SiYuan może zająć trochę czasu. Proszę poczekać kilka sekund lub kliknąć przycisk „Ponów próbę”",
@ -1387,6 +1388,7 @@
"_attrView": {
"table": "Tabela",
"gallery": "Karta",
"kanban": "Kanban",
"key": "Klucz główny",
"select": "Wybierz"
},

View file

@ -36,6 +36,7 @@
"showAllEntriesIcons": "Mostrar ícones de entradas",
"wrapAllFields": "Quebrar automaticamente os campos",
"gallery": "Cartão",
"kanban": "Kanban",
"newTag": "Nova tag",
"pleaseWait": "Por favor, aguarde...",
"reconnectPrompt": "Após alternar aplicativos, levará algum tempo para restaurar a operação do kernel SiYuan. Por favor, aguarde alguns segundos ou clique no botão \"Tentar novamente\"",
@ -1387,6 +1388,7 @@
"_attrView": {
"table": "Tabela",
"gallery": "Cartão",
"kanban": "Kanban",
"key": "Chave Primária",
"select": "Selecionar"
},

View file

@ -36,6 +36,7 @@
"showAllEntriesIcons": "Показать значки записей",
"wrapAllFields": "Автоматический перенос полей",
"gallery": "Карточка",
"kanban": "Канбан",
"newTag": "Новый тег",
"pleaseWait": "Пожалуйста, подождите...",
"reconnectPrompt": "После переключения приложений потребуется некоторое время, чтобы восстановить работу ядра SiYuan. Пожалуйста, подождите несколько секунд или нажмите кнопку «Повторить»",
@ -1387,6 +1388,7 @@
"_attrView": {
"table": "Таблица",
"gallery": "Карточка",
"kanban": "Канбан",
"key": "Первичный ключ",
"select": "Выбрать"
},

View file

@ -36,6 +36,7 @@
"showAllEntriesIcons": "顯示條目圖標",
"wrapAllFields": "欄位自動換行",
"gallery": "卡片",
"kanban": "看板",
"newTag": "新建標籤",
"pleaseWait": "請稍等片刻...",
"reconnectPrompt": "切換應用後再次進入需要一些時間恢復思源內核運行,請稍等幾秒或者點擊“重試”按鈕",
@ -1387,6 +1388,7 @@
"_attrView": {
"table": "表格",
"gallery": "卡片",
"kanban": "看板",
"key": "主鍵",
"select": "單選"
},

View file

@ -36,6 +36,7 @@
"showAllEntriesIcons": "显示条目图标",
"wrapAllFields": "字段自动换行",
"gallery": "卡片",
"kanban": "看板",
"newTag": "新建标签",
"pleaseWait": "请稍等片刻...",
"reconnectPrompt": "切换应用后再次进入需要一些时间恢复思源内核运行,请稍等几秒或者点击“重试”按钮",
@ -1387,6 +1388,7 @@
"_attrView": {
"table": "表格",
"gallery": "卡片",
"kanban": "看板",
"key": "主键",
"select": "单选"
},

View file

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<!--suppress XmlUnusedNamespaceDeclaration -->
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities">
<!-- use single quotes to avoid double quotes escaping in the publisher value -->
<Identity Name="89C2A984.SiYuan"
ProcessorArchitecture="arm64"
Publisher="CN=087C656E-C1D9-42D8-8807-CED45A74FC0F"
Version="3.3.2.0"/>
<Properties>
<DisplayName>SiYuan</DisplayName>
<PublisherDisplayName>云南链滴科技有限公司</PublisherDisplayName>
<Description>Refactor your thinking</Description>
<Logo>assets\StoreLogo.png</Logo>
</Properties>
<Resources>
<Resource Language="en-US"/>
<Resource Language="zh-CN"/>
</Resources>
<Dependencies>
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.14316.0" MaxVersionTested="10.0.14316.0"/>
</Dependencies>
<Capabilities>
<rescap:Capability Name="runFullTrust"/>
</Capabilities>
<Applications>
<Application Id="SiYuan" Executable="app\SiYuan.exe" EntryPoint="Windows.FullTrustApplication">
<uap:VisualElements
BackgroundColor="transparent"
DisplayName="SiYuan"
Square150x150Logo="assets\Square150x150Logo.png"
Square44x44Logo="assets\Square44x44Logo.png"
Description="Refactor your thinking">
<uap:DefaultTile Wide310x150Logo="assets\Wide310x150Logo.png"/>
</uap:VisualElements>
<Extensions>
<uap:Extension Category="windows.protocol">
<uap:Protocol Name="siyuan"/>
</uap:Extension>
</Extensions>
</Application>
</Applications>
</Package>

View file

@ -18,7 +18,6 @@
"build:export": "webpack --mode production --config webpack.export.js",
"gen:types": "tsc -d",
"start": "NODE_ENV=development electron ./electron/main.js",
"dist-appx": "ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ electron-builder --config electron-appx-builder.yml",
"dist": "ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ electron-builder --config electron-builder.yml --publish=never",
"dist-arm64": "ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ electron-builder --arm64 --config electron-builder-arm64.yml --publish=never",
"dist-darwin": "ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ electron-builder --mac --config electron-builder-darwin.yml --publish=never",

View file

@ -180,43 +180,43 @@ export class Asset extends Model {
<button id="previous" class="secondaryToolbarButton b3-menu__item pageUp">
<svg class="b3-menu__icon"><use xlink:href="#iconUp"></use></svg>
<span class="b3-menu__label">${window.siyuan.languages.previousLabel}</span>
<span class="b3-menu__accelerator">${updateHotkeyTip("P")}/${updateHotkeyTip("K")}</span>
<span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip("P")}/${updateHotkeyTip("K")}</span>
</button>
<button id="next" class="secondaryToolbarButton b3-menu__item pageDown">
<svg class="b3-menu__icon"><use xlink:href="#iconDown"></use></svg>
<span class="b3-menu__label">${window.siyuan.languages.nextLabel}</span>
<span class="b3-menu__accelerator">${updateHotkeyTip("J")}/${updateHotkeyTip("N")}</span>
<span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip("J")}/${updateHotkeyTip("N")}</span>
</button>
<button id="firstPage" class="secondaryToolbarButton b3-menu__item firstPage">
<svg class="b3-menu__icon"><use xlink:href="#iconBack"></use></svg>
<span class="b3-menu__label">${window.siyuan.languages.firstPage}</span>
<span class="b3-menu__accelerator">Home</span>
<span class="b3-menu__accelerator b3-menu__accelerator--hotkey">Home</span>
</button>
<button id="lastPage" class="secondaryToolbarButton b3-menu__item lastPage">
<svg class="b3-menu__icon"><use xlink:href="#iconForward"></use></svg>
<span class="b3-menu__label">${window.siyuan.languages.lastPage}</span>
<span class="b3-menu__accelerator">End</span>
<span class="b3-menu__accelerator b3-menu__accelerator--hotkey">End</span>
</button>
<div class="horizontalToolbarSeparator b3-menu__separator"></div>
<button id="zoomOutButton" class="secondaryToolbarButton b3-menu__item zoomOut">
<svg class="b3-menu__icon"><use xlink:href="#iconLine"></use></svg>
<span class="b3-menu__label">${window.siyuan.languages.zoomOut}</span>
<span class="b3-menu__accelerator">${updateHotkeyTip("⌘-")}</span>
<span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip("⌘-")}</span>
</button>
<button id="zoomInButton" class="secondaryToolbarButton b3-menu__item zoomIn">
<svg class="b3-menu__icon"><use xlink:href="#iconAdd"></use></svg>
<span class="b3-menu__label">${window.siyuan.languages.zoomIn}</span>
<span class="b3-menu__accelerator">${updateHotkeyTip("⌘=")}</span>
<span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip("⌘=")}</span>
</button>
<button id="pageRotateCw" class="secondaryToolbarButton b3-menu__item rotateCw">
<svg class="b3-menu__icon"><use xlink:href="#iconRedo"></use></svg>
<span class="b3-menu__label">${window.siyuan.languages.rotateCw}</span>
<span class="b3-menu__accelerator">R</span>
<span class="b3-menu__accelerator b3-menu__accelerator--hotkey">R</span>
</button>
<button id="pageRotateCcw" class="secondaryToolbarButton b3-menu__item rotateCcw">
<svg class="b3-menu__icon"><use xlink:href="#iconUndo"></use></svg>
<span class="b3-menu__label">${window.siyuan.languages.rotateCcw}</span>
<span class="b3-menu__accelerator">R</span>
<span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip("⇧R")}</span>
</button>
<div class="horizontalToolbarSeparator b3-menu__separator"></div>
@ -224,12 +224,12 @@ export class Asset extends Model {
<button id="cursorSelectTool" class="secondaryToolbarButton b3-menu__item selectTool toggled">
<svg class="b3-menu__icon"><use xlink:href="#iconSelectText"></use></svg>
<span class="b3-menu__label">${window.siyuan.languages.cursorText}</span>
<span class="b3-menu__accelerator">S</span>
<span class="b3-menu__accelerator b3-menu__accelerator--hotkey">S</span>
</button>
<button id="cursorHandTool" class="secondaryToolbarButton b3-menu__item handTool">
<svg class="b3-menu__icon"><use xlink:href="#iconHand"></use></svg>
<span class="b3-menu__label">${window.siyuan.languages.cursorHand}</span>
<span class="b3-menu__accelerator">H</span>
<span class="b3-menu__accelerator b3-menu__accelerator--hotkey">H</span>
</button>
<div class="horizontalToolbarSeparator b3-menu__separator"></div>
<button id="scrollVertical" class="secondaryToolbarButton b3-menu__item scrollModeButtons scrollVertical toggled">
@ -262,7 +262,7 @@ export class Asset extends Model {
<button id="presentationMode" class="secondaryToolbarButton b3-menu__item presentationMode">
<svg class="b3-menu__icon"><use xlink:href="#iconPlay"></use></svg>
<span class="b3-menu__label">${window.siyuan.languages.presentationMode}</span>
<span class="b3-menu__accelerator">${updateHotkeyTip("⌥⌘P")}</span>
<span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip("⌥⌘P")}</span>
</button>
<div class="horizontalToolbarSeparator b3-menu__separator spreadModeButtons"></div>
<button id="documentProperties" class="secondaryToolbarButton b3-menu__item documentProperties">

View file

@ -607,11 +607,15 @@
& > span {
pointer-events: none;
display: block;
color: var(--b3-theme-on-surface);
&:not(:empty)::before {
content: "";
}
&::before {
counter-increment: linenumber;
content: counter(linenumber);
color: var(--b3-theme-on-surface);
display: block;
text-align: right;
white-space: nowrap;

View file

@ -432,7 +432,7 @@
}
.b3-menu {
&__accelerator {
&__accelerator--hotkey {
display: none;
}

View file

@ -150,7 +150,16 @@ export const insertEmptyBlock = (protyle: IProtyle, position: InsertPosition, id
if (blockElement.getAttribute("data-type") === "NodeListItem") {
newElement = genListItemElement(blockElement, 0, true) as HTMLDivElement;
orderIndex = parseInt(blockElement.parentElement.firstElementChild.getAttribute("data-marker"));
} else if (position === "beforebegin" && blockElement.previousElementSibling &&
blockElement.previousElementSibling.getAttribute("data-type") === "NodeHeading" &&
blockElement.previousElementSibling.getAttribute("fold") === "1") {
newElement = genHeadingElement(blockElement.previousElementSibling, false, true) as HTMLDivElement;
} else if (position === "afterend" && blockElement &&
blockElement.getAttribute("data-type") === "NodeHeading" &&
blockElement.getAttribute("fold") === "1") {
newElement = genHeadingElement(blockElement, false, true) as HTMLDivElement;
}
const parentOldHTML = blockElement.parentElement.outerHTML;
const newId = newElement.getAttribute("data-node-id");
blockElement.insertAdjacentElement(position, newElement);
@ -216,6 +225,17 @@ export const genEmptyElement = (zwsp = true, wbr = true, id?: string) => {
return element;
};
export const genHeadingElement = (headElement: Element, getHTML = false, addWbr = false) => {
const html = `<div data-subtype="${headElement.getAttribute("data-subtype")}" data-node-id="${Lute.NewNodeID()}" data-type="NodeHeading" class="${headElement.className}"><div contenteditable="true" spellcheck="false">${addWbr ? "<wbr>" : ""}</div><div class="protyle-attr" contenteditable="false">${Constants.ZWSP}</div></div>`;
if (getHTML) {
return html;
} else {
const tempElement = document.createElement("template");
tempElement.innerHTML = html;
return tempElement.content.firstElementChild;
}
};
export const getLangByType = (type: string) => {
let lang = type;
switch (type) {

View file

@ -13,13 +13,7 @@ import {showMessage} from "../../dialog/message";
import {fetchPost, fetchSyncPost} from "../../util/fetch";
import {openEmojiPanel, unicode2Emoji} from "../../emoji";
import {mountHelp, newNotebook} from "../../util/mount";
import {confirmDialog} from "../../dialog/confirmDialog";
import {
isNotCtrl,
isOnlyMeta,
setStorageVal,
updateHotkeyAfterTip
} from "../../protyle/util/compatibility";
import {isNotCtrl, isOnlyMeta, setStorageVal, updateHotkeyAfterTip} from "../../protyle/util/compatibility";
import {openFileById} from "../../editor/util";
import {
hasClosestByAttribute,
@ -178,18 +172,6 @@ export class Files extends Model {
event.stopPropagation();
event.preventDefault();
break;
} else if (type === "remove") {
confirmDialog(window.siyuan.languages.deleteOpConfirm,
`${window.siyuan.languages.confirmDelete} <b>${escapeHtml(target.parentElement.querySelector(".b3-list-item__text").textContent)}</b>?`, () => {
fetchPost("/api/notebook/removeNotebook", {
notebook: target.getAttribute("data-url"),
callback: Constants.CB_MOUNT_REMOVE
});
}, undefined, true);
window.siyuan.menus.menu.remove();
event.stopPropagation();
event.preventDefault();
break;
} else if (type === "open") {
fetchPost("/api/notebook/openNotebook", {
notebook: target.getAttribute("data-url")
@ -813,14 +795,14 @@ export class Files extends Model {
private genNotebook(item: INotebook) {
const emojiHTML = `<span class="b3-list-item__icon b3-tooltips b3-tooltips__e" aria-label="${window.siyuan.languages.changeIcon}">${unicode2Emoji(item.icon || window.siyuan.storage[Constants.LOCAL_IMAGES].note)}</span>`;
if (item.closed) {
return `<li data-type="open" data-url="${item.id}" class="b3-list-item b3-list-item--hide-action">
return `<li data-url="${item.id}" class="b3-list-item b3-list-item--hide-action">
<span class="b3-list-item__toggle fn__hidden">
<svg class="b3-list-item__arrow"><use xlink:href="#iconRight"></use></svg>
</span>
${emojiHTML}
<span class="b3-list-item__text">${escapeHtml(item.name)}</span>
<span data-type="remove" data-url="${item.id}" class="b3-list-item__action b3-tooltips b3-tooltips__w${(window.siyuan.config.readonly) ? " fn__none" : ""}" aria-label="${window.siyuan.languages.delete}">
<svg><use xlink:href="#iconTrashcan"></use></svg>
<span class="b3-list-item__text" style="cursor: default;">${escapeHtml(item.name)}</span>
<span data-type="open" data-url="${item.id}" class="b3-list-item__action b3-tooltips b3-tooltips__w${(window.siyuan.config.readonly) ? " fn__none" : ""}" aria-label="${window.siyuan.languages.openBy}">
<svg><use xlink:href="#iconOpen"></use></svg>
</span>
</li>`;
} else {
@ -1005,9 +987,12 @@ data-type="navigation-root" data-path="/">
}
if (sourceElement.parentElement.childElementCount === 1) {
if (sourceElement.parentElement.previousElementSibling) {
sourceElement.parentElement.previousElementSibling.querySelector(".b3-list-item__toggle").classList.add("fn__hidden");
sourceElement.parentElement.previousElementSibling.querySelector(".b3-list-item__arrow").classList.remove("b3-list-item__arrow--open");
const emojiElement = sourceElement.parentElement.previousElementSibling.querySelector(".b3-list-item__icon");
const parentLiElement = sourceElement.parentElement.previousElementSibling;
if (parentLiElement.getAttribute("data-type") !== "navigation-root") {
parentLiElement.querySelector(".b3-list-item__toggle").classList.add("fn__hidden");
}
parentLiElement.querySelector(".b3-list-item__arrow").classList.remove("b3-list-item__arrow--open");
const emojiElement = parentLiElement.querySelector(".b3-list-item__icon");
if (emojiElement.innerHTML === unicode2Emoji(window.siyuan.storage[Constants.LOCAL_IMAGES].folder)) {
emojiElement.innerHTML = unicode2Emoji(window.siyuan.storage[Constants.LOCAL_IMAGES].file);
}
@ -1016,6 +1001,12 @@ data-type="navigation-root" data-path="/">
} else {
sourceElement.remove();
}
} else {
const parentElement = this.element.querySelector(`ul[data-url="${response.data.fromNotebook}"] li[data-path="${pathPosix().dirname(response.data.fromPath)}.sy"]`) as HTMLElement;
if (parentElement && parentElement.getAttribute("data-count") === "1") {
parentElement.querySelector(".b3-list-item__toggle").classList.add("fn__hidden");
parentElement.querySelector(".b3-list-item__arrow").classList.remove("b3-list-item__arrow--open");
}
}
const newElement = this.element.querySelector(`[data-url="${response.data.toNotebook}"] li[data-path="${response.data.toPath}"]`) as HTMLElement;
// 更新移动到的新文件夹

View file

@ -750,7 +750,7 @@ export const newModelByInitData = (app: App, tab: Tab, json: any) => {
rootId: json.rootId,
blockId: json.blockId,
mode: json.mode,
action: typeof json.action === "string" ? [json.action] : json.action,
action: typeof json.action === "string" ? [json.action, Constants.CB_GET_FOCUS] : json.action.concat(Constants.CB_GET_FOCUS),
});
}
return model;

View file

@ -236,7 +236,7 @@ export class MenuItem {
html = `<svg class="b3-menu__icon ${options.iconClass || ""}" style="${options.icon === "iconClose" ? "height:10px;" : ""}"><use xlink:href="#${options.icon || ""}"></use></svg>${html}`;
}
if (options.accelerator) {
html += `<span class="b3-menu__accelerator">${updateHotkeyTip(options.accelerator)}</span>`;
html += `<span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip(options.accelerator)}</span>`;
}
if (options.action) {
html += `<svg class="b3-menu__action${options.action === "iconCloseRound" ? " b3-menu__action--close" : ""}"><use xlink:href="#${options.action}"></use></svg>`;

View file

@ -9,7 +9,6 @@ import {genUUID} from "../../util/genID";
import {openMobileFileById} from "../editor";
import {unicode2Emoji} from "../../emoji";
import {mountHelp, newNotebook} from "../../util/mount";
import {confirmDialog} from "../../dialog/confirmDialog";
import {newFile} from "../../util/newFile";
import {MenuItem} from "../../menus/Menu";
import {App} from "../../index";
@ -176,17 +175,6 @@ export class MobileFiles extends Model {
event.stopPropagation();
event.preventDefault();
break;
} else if (type === "remove") {
confirmDialog(window.siyuan.languages.deleteOpConfirm,
`${window.siyuan.languages.confirmDelete} <b>${escapeHtml(target.parentElement.querySelector(".b3-list-item__text").textContent)}</b>?`, () => {
fetchPost("/api/notebook/removeNotebook", {
notebook: target.getAttribute("data-url"),
callback: Constants.CB_MOUNT_REMOVE
});
}, undefined, true);
event.stopPropagation();
event.preventDefault();
break;
} else if (type === "open") {
fetchPost("/api/notebook/openNotebook", {
notebook: target.getAttribute("data-url")
@ -309,14 +297,14 @@ export class MobileFiles extends Model {
private genNotebook(item: INotebook) {
const emojiHTML = `<span class="b3-list-item__icon b3-tooltips b3-tooltips__e" aria-label="${window.siyuan.languages.changeIcon}">${unicode2Emoji(item.icon || window.siyuan.storage[Constants.LOCAL_IMAGES].note)}</span>`;
if (item.closed) {
return `<li data-type="open" data-url="${item.id}" class="b3-list-item">
return `<li data-url="${item.id}" class="b3-list-item">
<span class="b3-list-item__toggle fn__hidden">
<svg class="b3-list-item__arrow"><use xlink:href="#iconRight"></use></svg>
</span>
${emojiHTML}
<span class="b3-list-item__text">${escapeHtml(item.name)}</span>
<span data-type="remove" data-url="${item.id}" class="b3-list-item__action${(window.siyuan.config.readonly) ? " fn__none" : ""}">
<svg><use xlink:href="#iconTrashcan"></use></svg>
<span data-type="open" data-url="${item.id}" class="b3-list-item__action${(window.siyuan.config.readonly) ? " fn__none" : ""}">
<svg><use xlink:href="#iconOpen"></use></svg>
</span>
</li>`;
} else {
@ -402,6 +390,12 @@ export class MobileFiles extends Model {
} else {
sourceElement.remove();
}
} else {
const parentElement = this.element.querySelector(`ul[data-url="${data.fromNotebook}"] li[data-path="${pathPosix().dirname(data.fromPath)}.sy"]`) as HTMLElement;
if (parentElement && parentElement.getAttribute("data-count") === "1") {
parentElement.querySelector(".b3-list-item__toggle").classList.add("fn__hidden");
parentElement.querySelector(".b3-list-item__arrow").classList.remove("b3-list-item__arrow--open");
}
}
const newElement = this.element.querySelector(`[data-url="${data.toNotebook}"] li[data-path="${data.toPath}"]`) as HTMLElement;
// 重新展开移动到的新文件夹

View file

@ -752,7 +752,7 @@ export const popSearch = (app: App, searchConfig?: any) => {
<div class="toolbar">
<span class="fn__flex-1"></span>
<svg data-type="toggle-replace" class="toolbar__icon${config.hasReplace ? " toolbar__icon--active" : ""}"><use xlink:href="#iconReplace"></use></svg>
<svg ${enableIncludeChild ? "" : "disabled"} data-type="include" class="toolbar__icon${includeChild ? " toolbar__icon--active" : ""}"><use xlink:href="#iconCopy"></use></svg>
<svg ${enableIncludeChild ? "" : "disabled"} data-type="include" class="toolbar__icon${includeChild ? " toolbar__icon--active" : ""}"><use xlink:href="#iconInclude"></use></svg>
<svg data-type="path" class="toolbar__icon"><use xlink:href="#iconFolder"></use></svg>
<svg ${document.querySelector("#empty").classList.contains("fn__none") ? "" : "disabled"} data-type="currentPath" class="toolbar__icon"><use xlink:href="#iconFocus"></use></svg>
<svg data-type="expand" class="toolbar__icon${config.group === 0 ? " fn__none" : ""}"><use xlink:href="#iconExpand"></use></svg>

View file

@ -58,7 +58,7 @@ export class Breadcrumb {
<span class="protyle-breadcrumb__space"></span>
<button class="protyle-breadcrumb__icon fn__none ariaLabel" aria-label="${updateHotkeyTip(window.siyuan.config.keymap.editor.general.exitFocus.custom)}" data-type="exit-focus">${window.siyuan.languages.exitFocus}</button>
${padHTML}
<button class="block__icon fn__flex-center ariaLabel${window.siyuan.config.readonly ? " fn__none" : ""}" aria-label="${window.siyuan.languages.lockEdit}" data-type="readonly"><svg><use xlink:href="#iconUnlock"></use></svg></button>
<button class="block__icon fn__flex-center ariaLabel${window.siyuan.config.readonly ? " fn__none" : ""}" aria-label="${window.siyuan.languages.lockEdit}" data-type="readonly" data-subtype="unlock"><svg><use xlink:href="#iconUnlock"></use></svg></button>
<button class="block__icon fn__flex-center ariaLabel" data-type="doc" aria-label="${isMac() ? window.siyuan.languages.gutterTip2 : window.siyuan.languages.gutterTip2.replace("", "Shift+")}"><svg><use xlink:href="#iconFile"></use></svg></button>
<button class="block__icon fn__flex-center ariaLabel" data-type="more" aria-label="${window.siyuan.languages.more}"><svg><use xlink:href="#iconMore"></use></svg></button>
<button class="block__icon fn__flex-center fn__none ariaLabel" data-type="context" aria-label="${window.siyuan.languages.context}"><svg><use xlink:href="#iconAlignCenter"></use></svg></button>`;

View file

@ -80,6 +80,9 @@ export const exportImage = (id: string) => {
objectElement.remove();
}
}
previewElement.querySelectorAll(".protyle-linenumber__rows span").forEach((item, index) => {
item.textContent = (index + 1).toString();
});
setTimeout(() => {
addScript("/stage/protyle/js/html-to-image.min.js?v=1.11.13", "protyleHtml2image").then(async () => {
let blob = await window.htmlToImage.toBlob(exportDialog.element.querySelector(".b3-dialog__content"));

View file

@ -1802,15 +1802,6 @@ export class Gutter {
transferBlockRef(id);
}
}
window.siyuan.menus.menu.append(new MenuItem({
id: "jumpToParentNext",
label: window.siyuan.languages.jumpToParentNext,
accelerator: window.siyuan.config.keymap.editor.general.jumpToParentNext.custom,
click() {
hideElements(["select"], protyle);
jumpToParent(protyle, nodeElement, "next");
}
}).element);
window.siyuan.menus.menu.append(new MenuItem({
id: "jumpToParentPrev",
label: window.siyuan.languages.jumpToParentPrev,
@ -1820,6 +1811,15 @@ export class Gutter {
jumpToParent(protyle, nodeElement, "previous");
}
}).element);
window.siyuan.menus.menu.append(new MenuItem({
id: "jumpToParentNext",
label: window.siyuan.languages.jumpToParentNext,
accelerator: window.siyuan.config.keymap.editor.general.jumpToParentNext.custom,
click() {
hideElements(["select"], protyle);
jumpToParent(protyle, nodeElement, "next");
}
}).element);
window.siyuan.menus.menu.append(new MenuItem({
id: "jumpToParent",
label: window.siyuan.languages.jumpToParent,

View file

@ -23,7 +23,7 @@ import {avRender} from "../render/av/render";
const getHotkeyOrMarker = (hotkey: string, marker: string) => {
if (hotkey) {
return `<span class="b3-menu__accelerator">${updateHotkeyTip(hotkey)}</span>`;
return `<span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip(hotkey)}</span>`;
} else if (marker) {
return `<span class="b3-list-item__meta">${marker}</span>`;
}
@ -139,7 +139,7 @@ export const hintSlash = (key: string, protyle: IProtyle) => {
filter: [window.siyuan.languages.table, "table", "表格", "biaoge", "bg"],
id: "table",
value: `| ${Lute.Caret} | | |\n| --- | --- | --- |\n| | | |\n| | | |`,
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconTable"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.table}</span><span class="b3-menu__accelerator">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.table.custom))}</span></div>`,
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconTable"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.table}</span><span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.table.custom))}</span></div>`,
}, {
filter: [window.siyuan.languages.line, "thematic break", "divider", "分隔线", "分割线", "fengexian", "fgx"],
id: "line",
@ -168,62 +168,62 @@ export const hintSlash = (key: string, protyle: IProtyle) => {
filter: [window.siyuan.languages.link, "link", "a", "链接", "lianjie", "lj"],
id: "link",
value: "a",
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconLink"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.link}</span><span class="b3-menu__accelerator">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.link.custom))}</span></div>`,
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconLink"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.link}</span><span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.link.custom))}</span></div>`,
}, {
filter: [window.siyuan.languages.bold, "bold", "strong", "粗体", "cuti", "ct", "加粗", "jiacu", "jc"],
id: "bold",
value: "strong",
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconBold"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.bold}</span><span class="b3-menu__accelerator">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.bold.custom))}</span></div>`,
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconBold"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.bold}</span><span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.bold.custom))}</span></div>`,
}, {
filter: [window.siyuan.languages.italic, "italic", "em", "斜体", "xieti", "xt"],
id: "italic",
value: "em",
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconItalic"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.italic}</span><span class="b3-menu__accelerator">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.italic.custom))}</span></div>`,
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconItalic"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.italic}</span><span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.italic.custom))}</span></div>`,
}, {
filter: [window.siyuan.languages.underline, "underline", "下划线", "xiahuaxian", "xhx"],
id: "underline",
value: "u",
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconUnderline"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.underline}</span><span class="b3-menu__accelerator">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.underline.custom))}</span></div>`,
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconUnderline"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.underline}</span><span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.underline.custom))}</span></div>`,
}, {
filter: [window.siyuan.languages.strike, "strike", "delete", "删除线", "shanchuxian", "scx"],
id: "strike",
value: "s",
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconStrike"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.strike}</span><span class="b3-menu__accelerator">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.strike.custom))}</span></div>`,
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconStrike"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.strike}</span><span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.strike.custom))}</span></div>`,
}, {
filter: [window.siyuan.languages.mark, "mark", "标记", "biaoji", "bj", "高亮", "gaoliang", "gl"],
id: "mark",
value: "mark",
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconMark"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.mark}</span><span class="b3-menu__accelerator">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.mark.custom))}</span></div>`,
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconMark"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.mark}</span><span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.mark.custom))}</span></div>`,
}, {
filter: [window.siyuan.languages.sup, "superscript", "上标", "shangbiao", "sb"],
id: "sup",
value: "sup",
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconSup"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.sup}</span><span class="b3-menu__accelerator">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.sup.custom))}</span></div>`,
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconSup"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.sup}</span><span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.sup.custom))}</span></div>`,
}, {
filter: [window.siyuan.languages.sub, "subscript", "下标", "xiaobiao", "xb"],
id: "sub",
value: "sub",
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconSub"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.sub}</span><span class="b3-menu__accelerator">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.sub.custom))}</span></div>`,
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconSub"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.sub}</span><span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.sub.custom))}</span></div>`,
}, {
filter: [window.siyuan.languages["inline-code"], "inline code", "行级代码", "hangjidaima", "hjdm"],
id: "inlineCode",
value: "code",
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconInlineCode"></use></svg><span class="b3-list-item__text">${window.siyuan.languages["inline-code"]}</span><span class="b3-menu__accelerator">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert["inline-code"].custom))}</span></div>`,
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconInlineCode"></use></svg><span class="b3-list-item__text">${window.siyuan.languages["inline-code"]}</span><span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert["inline-code"].custom))}</span></div>`,
}, {
filter: [window.siyuan.languages.kbd, "kbd", "键盘", "jianpan", "jp"],
id: "kbd",
value: "kbd",
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconKeymap"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.kbd}</span><span class="b3-menu__accelerator">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.kbd.custom))}</span></div>`,
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconKeymap"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.kbd}</span><span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.kbd.custom))}</span></div>`,
}, {
filter: [window.siyuan.languages.tag, "tags", "标签", "biaoqian", "bq"],
id: "tag",
value: "tag",
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconTags"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.tag}</span><span class="b3-menu__accelerator">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.tag.custom))}</span></div>`,
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconTags"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.tag}</span><span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.tag.custom))}</span></div>`,
}, {
filter: [window.siyuan.languages["inline-math"], "inline formulas", "inline math", "行级公式", "hangjigongshi", "hjgs", "行级数学公式", "hangjishuxvegongshi", "hangjishuxuegongshi", "hjsxgs"],
id: "inlineMath",
value: "inline-math",
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconMath"></use></svg><span class="b3-list-item__text">${window.siyuan.languages["inline-math"]}</span><span class="b3-menu__accelerator">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert["inline-math"].custom))}</span></div>`,
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconMath"></use></svg><span class="b3-list-item__text">${window.siyuan.languages["inline-math"]}</span><span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert["inline-math"].custom))}</span></div>`,
}, {
value: "",
id: "separator_3",

View file

@ -335,7 +335,7 @@ export const genCellValue = (colType: TAVCol, value: string | any) => {
cellValue = {
type: colType,
number: {
content: null,
content: 0,
isNotEmpty: false
}
};
@ -863,6 +863,10 @@ export const updateCellsValue = (protyle: IProtyle, nodeElement: HTMLElement, va
doOperations.push(...operations.doOperations);
undoOperations.push(...operations.undoOperations);
}
// formattedContent 在单元格渲染时没有用到,需对比保持一致
if (type === "date") {
cellValue.date.formattedContent = oldValue.date.formattedContent;
}
if (objEquals(cellValue, oldValue)) {
return;
}

View file

@ -932,7 +932,7 @@ export const showColMenu = (protyle: IProtyle, blockElement: Element, cellElemen
});
menu.addItem({
icon: "iconSoftWrap",
label: `<label class="fn__flex" style="margin-bottom: 4px"><span>${window.siyuan.languages.wrap}</span><span class="fn__space fn__flex-1"></span>
label: `<label class="fn__flex fn__pointer"><span>${window.siyuan.languages.wrap}</span><span class="fn__space fn__flex-1"></span>
<input type="checkbox" class="b3-switch b3-switch--menu"${cellElement.dataset.wrap === "true" ? " checked" : ""}></label>`,
bind(element) {
const wrapElement = element.querySelector(".b3-switch") as HTMLInputElement;

View file

@ -2,7 +2,7 @@ import * as dayjs from "dayjs";
import {genCellValueByElement, updateCellsValue} from "./cell";
export const getDateHTML = (cellElements: HTMLElement[]) => {
const cellValue = genCellValueByElement("date", cellElements[0]).date;
const cellValue = genCellValueByElement("date", cellElements[0]).date;
const isNotTime = cellValue.isNotTime;
let value = "";
const currentDate = new Date().getTime();
@ -64,9 +64,9 @@ export const bindDateEvent = (options: {
}
if (event.key === "Enter") {
updateCellsValue(options.protyle, options.blockElement as HTMLElement, {
content: getFullYearTime(inputElements[0].dataset.value),
content: getFullYearTime(inputElements[0].dataset.value) || 0,
isNotEmpty: inputElements[0].value !== "",
content2: getFullYearTime(inputElements[1].dataset.value),
content2: getFullYearTime(inputElements[1].dataset.value) || 0,
isNotEmpty2: inputElements[1].value !== "",
hasEndDate: inputElements[2].checked,
isNotTime: !inputElements[3].checked,
@ -114,9 +114,9 @@ export const bindDateEvent = (options: {
});
return () => {
updateCellsValue(options.protyle, options.blockElement as HTMLElement, {
content: getFullYearTime(inputElements[0].dataset.value),
content: getFullYearTime(inputElements[0].dataset.value) || 0,
isNotEmpty: inputElements[0].value !== "",
content2: getFullYearTime(inputElements[1].dataset.value),
content2: getFullYearTime(inputElements[1].dataset.value) || 0,
isNotEmpty2: inputElements[1].value !== "",
hasEndDate: inputElements[2].checked,
isNotTime: !inputElements[3].checked,

View file

@ -154,6 +154,10 @@ const afterRenderGallery = (options: ITableOptions) => {
if (typeof options.resetData.oldOffset === "number") {
options.protyle.contentElement.scrollTop = options.resetData.oldOffset;
}
if (options.blockElement.getAttribute("data-need-focus") === "true") {
focusBlock(options.blockElement);
options.blockElement.removeAttribute("data-need-focus");
}
options.blockElement.setAttribute("data-render", "true");
if (options.resetData.alignSelf) {
options.blockElement.style.alignSelf = options.resetData.alignSelf;

View file

@ -39,7 +39,7 @@ import {
openViewMenu
} from "./view";
import {focusBlock} from "../../util/selection";
import {setPageSize} from "./row";
import {getFieldIdByCellElement, setPageSize} from "./row";
import {bindRelationEvent, getRelationHTML, openSearchAV, setRelationCell, updateRelation} from "./relation";
import {bindRollupData, getRollupHTML, goSearchRollupCol} from "./rollup";
import {updateCellsValue} from "./cell";
@ -157,7 +157,18 @@ export const openMenuPanel = (options: {
const menuElement = avPanelElement.lastElementChild as HTMLElement;
let tabRect = options.blockElement.querySelector(`.av__views, .av__row[data-col-id="${options.colId}"] > .block__logo`)?.getBoundingClientRect();
if (["select", "date", "asset", "relation", "rollup"].includes(options.type)) {
const cellRect = options.cellElements[options.cellElements.length - 1].getBoundingClientRect();
let lastElement = options.cellElements[options.cellElements.length - 1];
if (!options.blockElement.contains(lastElement)) {
// https://github.com/siyuan-note/siyuan/issues/15839
const rowID = getFieldIdByCellElement(lastElement, data.viewType);
if (data.viewType === "table") {
lastElement = options.blockElement.querySelector(`.av__row[data-id="${rowID}"] .av__cell[data-col-id="${lastElement.dataset.colId}"]`);
} else {
lastElement = options.blockElement.querySelector(`.av__gallery-item[data-id="${rowID}"] .av__cell[data-field-id="${lastElement.dataset.fieldId}"]`);
}
}
const cellRect = (lastElement || options.cellElements[options.cellElements.length - 1]).getBoundingClientRect();
if (options.type === "select") {
bindSelectEvent(options.protyle, data, menuElement, options.cellElements, options.blockElement);
} else if (options.type === "date") {
@ -1201,10 +1212,10 @@ export const openMenuPanel = (options: {
title: isTwoWay ? window.siyuan.languages.removeColConfirm : window.siyuan.languages.deleteOpConfirm,
content: `<div class="b3-dialog__content">
${isTwoWay ? window.siyuan.languages.confirmRemoveRelationField
.replace("${x}", menuElement.querySelector("input").value || window.siyuan.languages._kernel[272])
.replace("${y}", menuElement.querySelector('.b3-menu__item[data-type="goSearchAV"] .b3-menu__accelerator').textContent)
.replace("${z}", (menuElement.querySelector('input[data-type="colName"]') as HTMLInputElement).value || window.siyuan.languages._kernel[272])
: window.siyuan.languages.removeCol.replace("${x}", menuElement.querySelector("input").value || window.siyuan.languages._kernel[272])}
.replace("${x}", menuElement.querySelector("input").value || window.siyuan.languages._kernel[272])
.replace("${y}", menuElement.querySelector('.b3-menu__item[data-type="goSearchAV"] .b3-menu__accelerator').textContent)
.replace("${z}", (menuElement.querySelector('input[data-type="colName"]') as HTMLInputElement).value || window.siyuan.languages._kernel[272])
: window.siyuan.languages.removeCol.replace("${x}", menuElement.querySelector("input").value || window.siyuan.languages._kernel[272])}
<div class="fn__hr--b"></div>
<button class="fn__block b3-button b3-button--remove" data-action="delete">${isTwoWay ? window.siyuan.languages.removeBothRelationField : window.siyuan.languages.delete}</button>
<div class="fn__hr"></div>

View file

@ -282,6 +282,10 @@ const renderGroupTable = (options: ITableOptions) => {
};
const afterRenderTable = (options: ITableOptions) => {
if (options.blockElement.getAttribute("data-need-focus") === "true") {
focusBlock(options.blockElement);
options.blockElement.removeAttribute("data-need-focus");
}
options.blockElement.setAttribute("data-render", "true");
options.blockElement.querySelector(".av__scroll").scrollLeft = options.resetData.left;
options.blockElement.style.alignSelf = options.resetData.alignSelf;
@ -761,6 +765,7 @@ export const refreshAV = (protyle: IProtyle, operation: IOperation) => {
const attrElement = document.querySelector(`.b3-dialog--open[data-key="${Constants.DIALOG_ATTR}"] .custom-attr > [data-av-id="${avID}"]`) as HTMLElement;
if (attrElement) {
// 更新属性面板
attrElement.removeAttribute("data-rendering");
renderAVAttribute(attrElement.parentElement, attrElement.dataset.nodeId, protyle);
} else {
if (operation.action === "insertAttrViewBlock" && operation.context?.ignoreTip !== "true") {

View file

@ -59,7 +59,7 @@ const filterSelectHTML = (key: string, options: {
<span class="fn__ellipsis">${escapeHtml(key)}</span>
</span>
</div>
<span class="b3-menu__accelerator">${window.siyuan.languages.enterKey}</span>
<span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${window.siyuan.languages.enterKey}</span>
</button>${html}`;
} else if (html.indexOf("b3-menu__item--current") === -1) {
html = html.replace('class="b3-menu__item"', 'class="b3-menu__item b3-menu__item--current"');

View file

@ -14,7 +14,7 @@ export const mermaidRender = (element: Element, cdn = Constants.PROTYLE_CDN) =>
if (mermaidElements.length === 0) {
return;
}
addScript(`${cdn}/js/mermaid/mermaid.min.js?v=11.6.0`, "protyleMermaidScript").then(() => {
addScript(`${cdn}/js/mermaid/mermaid.min.js?v=11.11.0`, "protyleMermaidScript").then(() => {
const config: any = {
securityLevel: "loose", // 升级后无 https://github.com/siyuan-note/siyuan/issues/3587可使用该选项
altFontFamily: "sans-serif",

View file

@ -9,7 +9,7 @@ import {lineNumberRender} from "../render/highlightRender";
import {hideMessage, showMessage} from "../../dialog/message";
import {genUUID} from "../../util/genID";
import {getContenteditableElement, getLastBlock} from "../wysiwyg/getBlock";
import {genEmptyElement} from "../../block/util";
import {genEmptyElement, genHeadingElement} from "../../block/util";
import {transaction} from "../wysiwyg/transaction";
import {focusByRange} from "../util/selection";
/// #if !MOBILE
@ -137,16 +137,22 @@ export const initUI = (protyle: IProtyle) => {
return;
}
}
const lastRect = protyle.wysiwyg.element.lastElementChild.getBoundingClientRect();
const lastElement = protyle.wysiwyg.element.lastElementChild;
const lastRect = lastElement.getBoundingClientRect();
const range = document.createRange();
if (event.y > lastRect.bottom) {
const lastEditElement = getContenteditableElement(getLastBlock(protyle.wysiwyg.element.lastElementChild));
const lastEditElement = getContenteditableElement(getLastBlock(lastElement));
if (!protyle.options.click.preventInsetEmptyBlock && (
!lastEditElement ||
(protyle.wysiwyg.element.lastElementChild.getAttribute("data-type") !== "NodeParagraph" && protyle.wysiwyg.element.getAttribute("data-doc-type") !== "NodeListItem") ||
(protyle.wysiwyg.element.lastElementChild.getAttribute("data-type") === "NodeParagraph" && getContenteditableElement(lastEditElement).innerHTML !== ""))
(lastElement.getAttribute("data-type") !== "NodeParagraph" && protyle.wysiwyg.element.getAttribute("data-doc-type") !== "NodeListItem") ||
(lastElement.getAttribute("data-type") === "NodeParagraph" && getContenteditableElement(lastEditElement).innerHTML !== ""))
) {
const emptyElement = genEmptyElement(false, false);
let emptyElement:Element;
if (lastElement.getAttribute("data-type") === "NodeHeading" && lastElement.getAttribute("fold") === "1") {
emptyElement = genHeadingElement(lastElement) as Element;
} else {
emptyElement = genEmptyElement(false, false);
}
protyle.wysiwyg.element.insertAdjacentElement("beforeend", emptyElement);
transaction(protyle, [{
action: "insert",

View file

@ -368,8 +368,10 @@ export const disabledProtyle = (protyle: IProtyle) => {
item.setAttribute("draggable", "false");
});
if (protyle.breadcrumb) {
protyle.breadcrumb.element.parentElement.querySelector('[data-type="readonly"] use').setAttribute("xlink:href", "#iconLock");
protyle.breadcrumb.element.parentElement.querySelector('[data-type="readonly"]').setAttribute("aria-label", window.siyuan.config.editor.readOnly ? window.siyuan.languages.tempUnlock : window.siyuan.languages.unlockEdit);
const readonlyButton = protyle.breadcrumb.element.parentElement.querySelector('[data-type="readonly"]');
readonlyButton.querySelector("use").setAttribute("xlink:href", "#iconLock");
readonlyButton.setAttribute("aria-label", window.siyuan.config.editor.readOnly ? window.siyuan.languages.tempUnlock : window.siyuan.languages.unlockEdit);
readonlyButton.setAttribute("data-subtype", "lock");
const undoElement = protyle.breadcrumb.element.parentElement.querySelector('[data-type="undo"]');
if (undoElement && !undoElement.classList.contains("fn__none")) {
undoElement.classList.add("fn__none");
@ -426,8 +428,10 @@ export const enableProtyle = (protyle: IProtyle) => {
}
});
if (protyle.breadcrumb) {
protyle.breadcrumb.element.parentElement.querySelector('[data-type="readonly"] use').setAttribute("xlink:href", "#iconUnlock");
protyle.breadcrumb.element.parentElement.querySelector('[data-type="readonly"]').setAttribute("aria-label", window.siyuan.config.editor.readOnly ? window.siyuan.languages.cancelTempUnlock : window.siyuan.languages.lockEdit);
const readonlyButton = protyle.breadcrumb.element.parentElement.querySelector('[data-type="readonly"]');
readonlyButton.querySelector("use").setAttribute("xlink:href", "#iconUnlock");
readonlyButton.setAttribute("aria-label", window.siyuan.config.editor.readOnly ? window.siyuan.languages.cancelTempUnlock : window.siyuan.languages.lockEdit);
readonlyButton.setAttribute("data-subtype", "unlock");
const undoElement = protyle.breadcrumb.element.parentElement.querySelector('[data-type="undo"]');
if (undoElement && undoElement.classList.contains("fn__none")) {
undoElement.classList.remove("fn__none");

View file

@ -610,6 +610,7 @@ export const focusBlock = (element: Element, parentElement?: HTMLElement, toStar
range.setStart(cursorElement.firstChild, 0);
setRange = true;
} else {
element.setAttribute("data-need-focus", "true");
return false;
}
/// #else

View file

@ -1,4 +1,4 @@
import {genEmptyElement, insertEmptyBlock} from "../../block/util";
import {genEmptyElement, genHeadingElement, insertEmptyBlock} from "../../block/util";
import {focusByRange, focusByWbr, getSelectionOffset, setLastNodeRange} from "../util/selection";
import {
getContenteditableElement,
@ -220,7 +220,11 @@ export const enter = (blockElement: HTMLElement, range: Range, protyle: IProtyle
}
const id = blockElement.getAttribute("data-node-id");
const newElement = document.createElement("div");
newElement.appendChild(genEmptyElement(false, false));
if (blockElement.getAttribute("data-type") === "NodeHeading" && blockElement.getAttribute("fold") === "1") {
newElement.innerHTML = genHeadingElement(blockElement, true) as string;
} else {
newElement.appendChild(genEmptyElement(false, false));
}
const newEditableElement = newElement.querySelector('[contenteditable="true"]');
newEditableElement.appendChild(range.extractContents());
const selectWbrElement = newEditableElement.querySelector("wbr");

View file

@ -1741,7 +1741,7 @@ export class WYSIWYG {
}
const range = getSelection().getRangeAt(0);
if (this.element === range.startContainer || this.element.contains(range.startContainer)) {
protyle.toolbar.range = range;
protyle.toolbar.range = range.cloneRange();
}
});

View file

@ -300,6 +300,14 @@ export const removeBlock = async (protyle: IProtyle, blockElement: Element, rang
return;
}
if (blockType === "NodeHeading") {
if ((blockElement.previousElementSibling &&
blockElement.previousElementSibling.getAttribute("data-type") === "NodeHeading" &&
blockElement.previousElementSibling.getAttribute("fold") === "1") ||
(blockElement.getAttribute("data-type") === "NodeHeading" &&
blockElement.getAttribute("fold") === "1")) {
focusBlock(blockElement.previousElementSibling, undefined, false);
return;
}
turnsIntoTransaction({
protyle: protyle,
selectsElement: [blockElement],

View file

@ -106,7 +106,7 @@ export const genSearch = (app: App, config: Config.IUILayoutTabSearchConfig, ele
</span>
<span class="fn__space"></span>
<span data-position="9south" id="searchInclude" ${enableIncludeChild ? "" : "disabled"} aria-label="${window.siyuan.languages.includeChildDoc}" class="block__icon block__icon--show ariaLabel">
<svg${includeChild ? ' class="ft__primary"' : ""}><use xlink:href="#iconCopy"></use></svg>
<svg${includeChild ? ' class="ft__primary"' : ""}><use xlink:href="#iconInclude"></use></svg>
</span>
<span class="fn__space"></span>
<span id="searchPath" aria-label="${window.siyuan.languages.specifyPath}" class="block__icon block__icon--show ariaLabel" data-position="9south">
@ -494,6 +494,11 @@ export const genSearch = (app: App, config: Config.IUILayoutTabSearchConfig, ele
event.preventDefault();
break;
} else if (target.id === "searchInclude") {
event.stopPropagation();
event.preventDefault();
if (target.hasAttribute("disabled")) {
return;
}
const svgElement = target.firstElementChild;
svgElement.classList.toggle("ft__primary");
if (!svgElement.classList.contains("ft__primary")) {
@ -511,8 +516,6 @@ export const genSearch = (app: App, config: Config.IUILayoutTabSearchConfig, ele
}
config.page = 1;
inputEvent(element, config, edit, true);
event.stopPropagation();
event.preventDefault();
break;
} else if (target.id === "searchReplace") {
// ctrl+P 不需要保存

File diff suppressed because one or more lines are too long

View file

@ -675,7 +675,41 @@ func renderHistoryAttributeView(c *gin.Context) {
id := arg["id"].(string)
created := arg["created"].(string)
view, attrView, err := model.RenderHistoryAttributeView(id, created)
blockIDArg := arg["blockID"]
var blockID string
if nil != blockIDArg {
blockID = blockIDArg.(string)
}
viewIDArg := arg["viewID"]
var viewID string
if nil != viewIDArg {
viewID = viewIDArg.(string)
}
page := 1
pageArg := arg["page"]
if nil != pageArg {
page = int(pageArg.(float64))
}
pageSize := -1
pageSizeArg := arg["pageSize"]
if nil != pageSizeArg {
pageSize = int(pageSizeArg.(float64))
}
query := ""
queryArg := arg["query"]
if nil != queryArg {
query = queryArg.(string)
}
groupPaging := map[string]interface{}{}
groupPagingArg := arg["groupPaging"]
if nil != groupPagingArg {
groupPaging = groupPagingArg.(map[string]interface{})
}
view, attrView, err := model.RenderHistoryAttributeView(blockID, id, viewID, query, page, pageSize, groupPaging, created)
if err != nil {
ret.Code = -1
ret.Msg = err.Error()

View file

@ -207,6 +207,7 @@ type View struct {
LayoutType LayoutType `json:"type"` // 当前布局类型
Table *LayoutTable `json:"table,omitempty"` // 表格布局
Gallery *LayoutGallery `json:"gallery,omitempty"` // 卡片布局
Kanban *LayoutKanban `json:"kanban,omitempty"` // 看板布局
ItemIDs []string `json:"itemIds,omitempty"` // 项目 ID 列表,用于维护所有项目
Group *ViewGroup `json:"group,omitempty"` // 分组规则
@ -221,6 +222,10 @@ type View struct {
GroupSort int `json:"groupSort"` // 分组排序值,用于手动排序
}
func (view *View) IsGroupView() bool {
return nil != view.Group && "" != view.Group.Field
}
// GetGroupValue 获取分组视图的分组值。
func (view *View) GetGroupValue() string {
if nil == view.GroupVal {
@ -270,7 +275,7 @@ func (view *View) RemoveGroupByID(groupID string) {
// GetGroupKey 获取分组视图的分组字段。
func (view *View) GetGroupKey(attrView *AttributeView) (ret *Key) {
if nil == view.Group || "" == view.Group.Field {
if !view.IsGroupView() {
return
}
@ -295,14 +300,15 @@ type LayoutType string
const (
LayoutTypeTable LayoutType = "table" // 属性视图类型 - 表格
LayoutTypeGallery LayoutType = "gallery" // 属性视图类型 - 卡片
LayoutTypeKanban LayoutType = "kanban" // 属性视图类型 - 看板
)
const (
ViewDefaultPageSize = 50 // 视图默认分页大小
)
func NewTableView() (ret *View) {
ret = &View{
func NewTableView() *View {
return &View{
ID: ast.NewNodeID(),
Name: GetAttributeViewI18n("table"),
Filters: []*ViewFilter{},
@ -311,7 +317,6 @@ func NewTableView() (ret *View) {
LayoutType: LayoutTypeTable,
Table: NewLayoutTable(),
}
return
}
func NewTableViewWithBlockKey(blockKeyID string) (view *View, blockKey, selectKey *Key) {
@ -334,7 +339,7 @@ func NewTableViewWithBlockKey(blockKeyID string) (view *View, blockKey, selectKe
}
func NewGalleryView() (ret *View) {
ret = &View{
return &View{
ID: ast.NewNodeID(),
Name: GetAttributeViewI18n("gallery"),
Filters: []*ViewFilter{},
@ -343,7 +348,18 @@ func NewGalleryView() (ret *View) {
LayoutType: LayoutTypeGallery,
Gallery: NewLayoutGallery(),
}
return
}
func NewKanbanView() (ret *View) {
return &View{
ID: ast.NewNodeID(),
Name: GetAttributeViewI18n("kanban"),
Filters: []*ViewFilter{},
Sorts: []*ViewSort{},
PageSize: ViewDefaultPageSize,
LayoutType: LayoutTypeKanban,
Kanban: NewLayoutKanban(),
}
}
// Viewable 描述了视图的接口。
@ -530,6 +546,15 @@ func SaveAttributeView(av *AttributeView) (err error) {
}
}
// 清理渲染回填值
for _, kv := range av.KeyValues {
for i := len(kv.Values) - 1; i >= 0; i-- {
if kv.Values[i].IsRenderAutoFill {
kv.Values = append(kv.Values[:i], kv.Values[i+1:]...)
}
}
}
var data []byte
if util.UseSingleLineSave {
data, err = gulu.JSON.MarshalJSON(av)
@ -741,6 +766,11 @@ func (av *AttributeView) Clone() (ret *AttributeView) {
for _, cardField := range view.Gallery.CardFields {
cardField.ID = keyIDMap[cardField.ID]
}
case LayoutTypeKanban:
view.Kanban.ID = ast.NewNodeID()
for _, field := range view.Kanban.Fields {
field.ID = keyIDMap[field.ID]
}
}
view.ItemIDs = []string{}
}

View file

@ -81,6 +81,9 @@ func NewViewBaseInstance(view *View) *BaseInstance {
case LayoutTypeGallery:
showIcon = view.Gallery.ShowIcon
wrapField = view.Gallery.WrapField
case LayoutTypeKanban:
showIcon = view.Kanban.ShowIcon
wrapField = view.Kanban.WrapField
}
return &BaseInstance{
ID: view.ID,

168
kernel/av/layout_kanban.go Normal file
View file

@ -0,0 +1,168 @@
// SiYuan - Refactor your thinking
// Copyright (c) 2020-present, b3log.org
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package av
import (
"github.com/88250/lute/ast"
)
// LayoutKanban 描述了看板视图的结构。
type LayoutKanban struct {
*BaseLayout
CoverFrom CoverFrom `json:"coverFrom"` // 封面来源01内容图2资源字段
CoverFromAssetKeyID string `json:"coverFromAssetKeyID,omitempty"` // 资源字段 IDCoverFrom 为 2 时有效
CardAspectRatio CardAspectRatio `json:"cardAspectRatio"` // 卡片宽高比
CardSize CardSize `json:"cardSize"` // 卡片大小0小卡片1中卡片2大卡片
FitImage bool `json:"fitImage"` // 是否适应封面图片大小
DisplayFieldName bool `json:"displayFieldName"` // 是否显示字段名称
Fields []*ViewKanbanField `json:"fields"` // 字段
}
func NewLayoutKanban() *LayoutKanban {
return &LayoutKanban{
BaseLayout: &BaseLayout{
Spec: 0,
ID: ast.NewNodeID(),
ShowIcon: true,
},
CoverFrom: CoverFromContentImage,
CardAspectRatio: CardAspectRatio16_9,
CardSize: CardSizeMedium,
}
}
// ViewKanbanField 描述了看板字段的结构。
type ViewKanbanField struct {
*BaseField
}
// Kanban 描述了看板视图实例的结构。
type Kanban struct {
*BaseInstance
CoverFrom CoverFrom `json:"coverFrom"` // 封面来源
CoverFromAssetKeyID string `json:"coverFromAssetKeyID,omitempty"` // 资源字段 IDCoverFrom 为 CoverFromAssetField 时有效
CardAspectRatio CardAspectRatio `json:"cardAspectRatio"` // 卡片宽高比
CardSize CardSize `json:"cardSize"` // 卡片大小
FitImage bool `json:"fitImage"` // 是否适应封面图片大小
DisplayFieldName bool `json:"displayFieldName"` // 是否显示字段名称
Fields []*KanbanField `json:"fields"` // 卡片字段
Cards []*KanbanCard `json:"cards"` // 卡片
CardCount int `json:"rowCount"` // 总卡片数
}
// KanbanCard 描述了看板实例卡片的结构。
type KanbanCard struct {
ID string `json:"id"` // 卡片 ID
Values []*KanbanFieldValue `json:"values"` // 卡片字段值
CoverURL string `json:"coverURL"` // 卡片封面超链接
CoverContent string `json:"coverContent"` // 卡片封面文本内容
}
// KanbanField 描述了看板实例字段的结构。
type KanbanField struct {
*BaseInstanceField
}
// KanbanFieldValue 描述了卡片字段实例值的结构。
type KanbanFieldValue struct {
*BaseValue
}
func (card *KanbanCard) GetID() string {
return card.ID
}
func (card *KanbanCard) GetBlockValue() (ret *Value) {
for _, v := range card.Values {
if KeyTypeBlock == v.ValueType {
ret = v.Value
break
}
}
return
}
func (card *KanbanCard) GetValues() (ret []*Value) {
ret = []*Value{}
for _, v := range card.Values {
ret = append(ret, v.Value)
}
return
}
func (card *KanbanCard) GetValue(keyID string) (ret *Value) {
for _, value := range card.Values {
if nil != value.Value && keyID == value.Value.KeyID {
ret = value.Value
break
}
}
return
}
func (kanban *Kanban) GetItems() (ret []Item) {
ret = []Item{}
for _, card := range kanban.Cards {
ret = append(ret, card)
}
return
}
func (kanban *Kanban) SetItems(items []Item) {
kanban.Cards = []*KanbanCard{}
for _, item := range items {
kanban.Cards = append(kanban.Cards, item.(*KanbanCard))
}
}
func (kanban *Kanban) CountItems() int {
return len(kanban.Cards)
}
func (kanban *Kanban) GetFields() (ret []Field) {
ret = []Field{}
for _, field := range kanban.Fields {
ret = append(ret, field)
}
return ret
}
func (kanban *Kanban) GetField(id string) (ret Field, fieldIndex int) {
for i, field := range kanban.Fields {
if field.ID == id {
return field, i
}
}
return nil, -1
}
func (kanban *Kanban) GetValue(itemID, keyID string) (ret *Value) {
for _, card := range kanban.Cards {
if card.ID == itemID {
return card.GetValue(keyID)
}
}
return nil
}
func (kanban *Kanban) GetType() LayoutType {
return LayoutTypeKanban
}

View file

@ -57,6 +57,8 @@ type Value struct {
Checkbox *ValueCheckbox `json:"checkbox,omitempty"`
Relation *ValueRelation `json:"relation,omitempty"`
Rollup *ValueRollup `json:"rollup,omitempty"`
IsRenderAutoFill bool `json:"-"` // 标识是否是渲染阶段自动填充的值,保存数据的时候要删掉
}
func (value *Value) SetUpdatedAt(mills int64) {
@ -1199,7 +1201,7 @@ func (r *ValueRollup) calcContents(calc *RollupCalc, destKey *Key) {
}
}
func GetAttributeViewDefaultValue(valueID, keyID, blockID string, typ KeyType) (ret *Value) {
func GetAttributeViewDefaultValue(valueID, keyID, blockID string, typ KeyType, keyDateAutoFill bool) (ret *Value) {
if "" == valueID {
valueID = ast.NewNodeID()
}
@ -1225,7 +1227,7 @@ func GetAttributeViewDefaultValue(valueID, keyID, blockID string, typ KeyType) (
case KeyTypeNumber:
ret.Number = &ValueNumber{}
case KeyTypeDate:
ret.Date = &ValueDate{IsNotTime: true}
ret.Date = &ValueDate{IsNotTime: !keyDateAutoFill}
case KeyTypeSelect:
ret.MSelect = []*ValueSelect{}
case KeyTypeMSelect:

View file

@ -206,6 +206,12 @@ func DocIAL(absPath string) (ret map[string]string) {
return
}
func TreeSize(tree *parse.Tree) (size uint64) {
luteEngine := util.NewLute() // 不关注用户的自定义解析渲染选项
renderer := render.NewJSONRenderer(tree, luteEngine.RenderOptions)
return uint64(len(renderer.Render()))
}
func WriteTree(tree *parse.Tree) (size uint64, err error) {
data, filePath, err := prepareWriteTree(tree)
if err != nil {

View file

@ -101,7 +101,7 @@ func GetAttrViewAddingBlockDefaultValues(avID, viewID, groupID, previousBlockID,
return
}
if 1 > len(view.Filters) && nil == view.Group {
if 1 > len(view.Filters) && !view.IsGroupView() {
// 没有过滤条件也没有分组条件时忽略
return
}
@ -128,7 +128,7 @@ func GetAttrViewAddingBlockDefaultValues(avID, viewID, groupID, previousBlockID,
func getAttrViewAddingBlockDefaultValues(attrView *av.AttributeView, view, groupView *av.View, previousItemID, addingItemID string) (ret map[string]*av.Value) {
ret = map[string]*av.Value{}
if 1 > len(view.Filters) && nil == view.Group {
if 1 > len(view.Filters) && !view.IsGroupView() {
// 没有过滤条件也没有分组条件时忽略
return
}
@ -233,7 +233,7 @@ func getAttrViewAddingBlockDefaultValues(attrView *av.AttributeView, view, group
}
}
} else {
newValue = av.GetAttributeViewDefaultValue(ast.NewNodeID(), groupKey.ID, addingItemID, groupKey.Type)
newValue = av.GetAttributeViewDefaultValue(ast.NewNodeID(), groupKey.ID, addingItemID, groupKey.Type, false)
newValue.MSelect = append(newValue.MSelect, &av.ValueSelect{Content: opt.Name, Color: opt.Color})
}
}
@ -278,7 +278,7 @@ func getAttrViewAddingBlockDefaultValues(attrView *av.AttributeView, view, group
if nil == nearItem && !filterKeyIDs[groupKey.ID] {
// 没有临近项并且分组字段和过滤字段不同时,使用分组值
newValue = av.GetAttributeViewDefaultValue(ast.NewNodeID(), groupKey.ID, addingItemID, groupKey.Type)
newValue = av.GetAttributeViewDefaultValue(ast.NewNodeID(), groupKey.ID, addingItemID, groupKey.Type, false)
if av.KeyTypeText == groupView.GroupVal.Type {
content := groupView.GroupVal.Text.Content
switch newValue.Type {
@ -429,7 +429,7 @@ func syncAttrViewTableColWidth(operation *Operation) (err error) {
break
}
}
case av.LayoutTypeGallery:
case av.LayoutTypeGallery, av.LayoutTypeKanban:
return
}
@ -533,7 +533,7 @@ func foldAttrViewGroup(avID, blockID, groupID string, folded bool) (err error) {
return err
}
if nil == view.Group {
if !view.IsGroupView() {
return
}
@ -667,6 +667,8 @@ func setAttrViewCardAspectRatio(operation *Operation) (err error) {
return
case av.LayoutTypeGallery:
view.Gallery.CardAspectRatio = av.CardAspectRatio(operation.Data.(float64))
case av.LayoutTypeKanban:
view.Kanban.CardAspectRatio = av.CardAspectRatio(operation.Data.(float64))
}
err = av.SaveAttributeView(attrView)
@ -707,7 +709,7 @@ func ChangeAttrViewLayout(blockID, avID string, layout av.LayoutType) (err error
switch newLayout {
case av.LayoutTypeTable:
if view.Name == av.GetAttributeViewI18n("gallery") {
if view.Name == av.GetAttributeViewI18n("gallery") || view.Name == av.GetAttributeViewI18n("kanban") {
view.Name = av.GetAttributeViewI18n("table")
}
@ -721,9 +723,13 @@ func ChangeAttrViewLayout(blockID, avID string, layout av.LayoutType) (err error
for _, field := range view.Gallery.CardFields {
view.Table.Columns = append(view.Table.Columns, &av.ViewTableColumn{BaseField: &av.BaseField{ID: field.ID}})
}
case av.LayoutTypeKanban:
for _, field := range view.Kanban.Fields {
view.Table.Columns = append(view.Table.Columns, &av.ViewTableColumn{BaseField: &av.BaseField{ID: field.ID}})
}
}
case av.LayoutTypeGallery:
if view.Name == av.GetAttributeViewI18n("table") {
if view.Name == av.GetAttributeViewI18n("table") || view.Name == av.GetAttributeViewI18n("kanban") {
view.Name = av.GetAttributeViewI18n("gallery")
}
@ -737,6 +743,30 @@ func ChangeAttrViewLayout(blockID, avID string, layout av.LayoutType) (err error
for _, col := range view.Table.Columns {
view.Gallery.CardFields = append(view.Gallery.CardFields, &av.ViewGalleryCardField{BaseField: &av.BaseField{ID: col.ID}})
}
case av.LayoutTypeKanban:
for _, field := range view.Kanban.Fields {
view.Gallery.CardFields = append(view.Gallery.CardFields, &av.ViewGalleryCardField{BaseField: &av.BaseField{ID: field.ID}})
}
}
case av.LayoutTypeKanban:
if view.Name == av.GetAttributeViewI18n("table") || view.Name == av.GetAttributeViewI18n("gallery") {
view.Name = av.GetAttributeViewI18n("kanban")
}
if nil != view.Kanban {
break
}
view.Kanban = av.NewLayoutKanban()
switch view.LayoutType {
case av.LayoutTypeTable:
for _, col := range view.Table.Columns {
view.Kanban.Fields = append(view.Kanban.Fields, &av.ViewKanbanField{BaseField: &av.BaseField{ID: col.ID}})
}
case av.LayoutTypeGallery:
for _, field := range view.Gallery.CardFields {
view.Kanban.Fields = append(view.Kanban.Fields, &av.ViewKanbanField{BaseField: &av.BaseField{ID: field.ID}})
}
}
}
@ -816,6 +846,11 @@ func setAttrViewWrapField(operation *Operation) (err error) {
for _, field := range view.Gallery.CardFields {
field.Wrap = allFieldWrap
}
case av.LayoutTypeKanban:
view.Kanban.WrapField = allFieldWrap
for _, field := range view.Kanban.Fields {
field.Wrap = allFieldWrap
}
}
err = av.SaveAttributeView(attrView)
@ -846,6 +881,8 @@ func setAttrViewShowIcon(operation *Operation) (err error) {
view.Table.ShowIcon = operation.Data.(bool)
case av.LayoutTypeGallery:
view.Gallery.ShowIcon = operation.Data.(bool)
case av.LayoutTypeKanban:
view.Kanban.ShowIcon = operation.Data.(bool)
}
err = av.SaveAttributeView(attrView)
@ -876,6 +913,8 @@ func setAttrViewFitImage(operation *Operation) (err error) {
return
case av.LayoutTypeGallery:
view.Gallery.FitImage = operation.Data.(bool)
case av.LayoutTypeKanban:
view.Kanban.FitImage = operation.Data.(bool)
}
err = av.SaveAttributeView(attrView)
@ -906,6 +945,8 @@ func setAttrViewDisplayFieldName(operation *Operation) (err error) {
return
case av.LayoutTypeGallery:
view.Gallery.DisplayFieldName = operation.Data.(bool)
case av.LayoutTypeKanban:
view.Kanban.DisplayFieldName = operation.Data.(bool)
}
err = av.SaveAttributeView(attrView)
@ -936,6 +977,8 @@ func setAttrViewCardSize(operation *Operation) (err error) {
return
case av.LayoutTypeGallery:
view.Gallery.CardSize = av.CardSize(operation.Data.(float64))
case av.LayoutTypeKanban:
view.Kanban.CardSize = av.CardSize(operation.Data.(float64))
}
err = av.SaveAttributeView(attrView)
@ -966,6 +1009,8 @@ func setAttrViewCoverFromAssetKeyID(operation *Operation) (err error) {
return
case av.LayoutTypeGallery:
view.Gallery.CoverFromAssetKeyID = operation.KeyID
case av.LayoutTypeKanban:
view.Kanban.CoverFromAssetKeyID = operation.KeyID
}
err = av.SaveAttributeView(attrView)
@ -996,6 +1041,8 @@ func setAttrViewCoverFrom(operation *Operation) (err error) {
return
case av.LayoutTypeGallery:
view.Gallery.CoverFrom = av.CoverFrom(operation.Data.(float64))
case av.LayoutTypeKanban:
view.Kanban.CoverFrom = av.CoverFrom(operation.Data.(float64))
}
err = av.SaveAttributeView(attrView)
@ -1032,7 +1079,7 @@ func AppendAttributeViewDetachedBlocksWithValues(avID string, blocksValues [][]*
v.IsDetached = true
v.CreatedAt = now
v.UpdatedAt = now
v.IsRenderAutoFill = false
keyValues.Values = append(keyValues.Values, v)
if av.KeyTypeSelect == v.Type || av.KeyTypeMSelect == v.Type {
@ -1339,6 +1386,10 @@ func SearchAttributeView(keyword string, excludeAvIDs []string) (ret []*AvSearch
continue
}
if gulu.Str.Contains(id, excludeAvIDs) {
continue
}
if nil == avBlockRels[id] {
continue
}
@ -1398,7 +1449,8 @@ func SearchAttributeView(keyword string, excludeAvIDs []string) (ret []*AvSearch
}
node = treenode.GetNodeInTree(tree, bID)
if nil == node || "" == node.AttributeViewID {
if nil == node || "" == node.AttributeViewID || ast.NodeAttributeView != node.Type {
node = nil
continue
}
@ -1535,7 +1587,7 @@ func GetBlockAttributeViewKeys(nodeID string) (ret []*BlockAttributeViewKeys) {
keyValues = append(keyValues, kValues)
} else {
// 如果没有值,那么就补一个默认值
kValues.Values = append(kValues.Values, av.GetAttributeViewDefaultValue(itemID[:14]+ast.NewNodeID()[14:], kv.Key.ID, itemID, kv.Key.Type))
kValues.Values = append(kValues.Values, av.GetAttributeViewDefaultValue(itemID[:14]+ast.NewNodeID()[14:], kv.Key.ID, itemID, kv.Key.Type, false))
keyValues = append(keyValues, kValues)
}
}
@ -1651,7 +1703,7 @@ func GetBlockAttributeViewKeys(nodeID string) (ret []*BlockAttributeViewKeys) {
ial = map[string]string{}
}
if nil == kv.Values[0].Template {
kv.Values[0] = av.GetAttributeViewDefaultValue(kv.Values[0].ID, kv.Key.ID, nodeID, kv.Key.Type)
kv.Values[0] = av.GetAttributeViewDefaultValue(kv.Values[0].ID, kv.Key.ID, nodeID, kv.Key.Type, false)
}
var renderErr error
@ -1725,7 +1777,7 @@ func GetBlockAttributeViewKeys(nodeID string) (ret []*BlockAttributeViewKeys) {
}
func genAttrViewGroups(view *av.View, attrView *av.AttributeView) {
if nil == view.Group {
if !view.IsGroupView() {
return
}
@ -1912,6 +1964,9 @@ func genAttrViewGroups(view *av.View, attrView *av.AttributeView) {
case av.LayoutTypeGallery:
v = av.NewGalleryView()
v.Gallery = av.NewLayoutGallery()
case av.LayoutTypeKanban:
v = av.NewKanbanView()
v.Kanban = av.NewLayoutKanban()
default:
logging.LogWarnf("unknown layout type [%s] for group view", view.LayoutType)
return
@ -1969,7 +2024,7 @@ type GroupState struct {
func getAttrViewGroupStates(view *av.View) (groupStates map[string]*GroupState) {
groupStates = map[string]*GroupState{}
if nil == view.Group {
if !view.IsGroupView() {
return
}
@ -2308,6 +2363,8 @@ func updateAttributeViewColRelation(operation *Operation) (err error) {
v.Table.Columns = append(v.Table.Columns, &av.ViewTableColumn{BaseField: &av.BaseField{ID: operation.BackRelationKeyID}})
case av.LayoutTypeGallery:
v.Gallery.CardFields = append(v.Gallery.CardFields, &av.ViewGalleryCardField{BaseField: &av.BaseField{ID: operation.BackRelationKeyID}})
case av.LayoutTypeKanban:
v.Kanban.Fields = append(v.Kanban.Fields, &av.ViewKanbanField{BaseField: &av.BaseField{ID: operation.BackRelationKeyID}})
}
}
@ -2329,6 +2386,7 @@ func updateAttributeViewColRelation(operation *Operation) (err error) {
destVal.Relation = &av.ValueRelation{}
}
destVal.UpdatedAt = now
destVal.IsRenderAutoFill = false
}
destVal.Relation.BlockIDs = append(destVal.Relation.BlockIDs, srcVal.BlockID)
destVal.Relation.BlockIDs = gulu.Str.RemoveDuplicatedElem(destVal.Relation.BlockIDs)
@ -2522,6 +2580,8 @@ func (tx *Transaction) doDuplicateAttrViewView(operation *Operation) (ret *TxErr
view = av.NewTableView()
case av.LayoutTypeGallery:
view = av.NewGalleryView()
case av.LayoutTypeKanban:
view = av.NewKanbanView()
}
view.ID = operation.ID
@ -2590,6 +2650,25 @@ func (tx *Transaction) doDuplicateAttrViewView(operation *Operation) (ret *TxErr
view.Gallery.DisplayFieldName = masterView.Gallery.DisplayFieldName
view.Gallery.ShowIcon = masterView.Gallery.ShowIcon
view.Gallery.WrapField = masterView.Gallery.WrapField
case av.LayoutTypeKanban:
for _, field := range masterView.Kanban.Fields {
view.Kanban.Fields = append(view.Kanban.Fields, &av.ViewKanbanField{
BaseField: &av.BaseField{
ID: field.ID,
Wrap: field.Wrap,
Hidden: field.Hidden,
Desc: field.Desc,
},
})
}
view.Kanban.CoverFrom = masterView.Kanban.CoverFrom
view.Kanban.CoverFromAssetKeyID = masterView.Kanban.CoverFromAssetKeyID
view.Kanban.CardSize = masterView.Kanban.CardSize
view.Kanban.FitImage = masterView.Kanban.FitImage
view.Kanban.DisplayFieldName = masterView.Kanban.DisplayFieldName
view.Kanban.ShowIcon = masterView.Kanban.ShowIcon
view.Kanban.WrapField = masterView.Kanban.WrapField
}
view.ItemIDs = masterView.ItemIDs
@ -2655,6 +2734,10 @@ func addAttrViewView(avID, viewID, blockID string, layout av.LayoutType) (err er
for _, field := range firstView.Gallery.CardFields {
view.Table.Columns = append(view.Table.Columns, &av.ViewTableColumn{BaseField: &av.BaseField{ID: field.ID}})
}
case av.LayoutTypeKanban:
for _, field := range firstView.Kanban.Fields {
view.Table.Columns = append(view.Table.Columns, &av.ViewTableColumn{BaseField: &av.BaseField{ID: field.ID}})
}
}
case av.LayoutTypeGallery:
view = av.NewGalleryView()
@ -2667,6 +2750,22 @@ func addAttrViewView(avID, viewID, blockID string, layout av.LayoutType) (err er
for _, field := range firstView.Gallery.CardFields {
view.Gallery.CardFields = append(view.Gallery.CardFields, &av.ViewGalleryCardField{BaseField: &av.BaseField{ID: field.ID}})
}
case av.LayoutTypeKanban:
for _, field := range firstView.Kanban.Fields {
view.Gallery.CardFields = append(view.Gallery.CardFields, &av.ViewGalleryCardField{BaseField: &av.BaseField{ID: field.ID}})
}
}
case av.LayoutTypeKanban:
view = av.NewKanbanView()
switch firstView.LayoutType {
case av.LayoutTypeTable:
for _, col := range firstView.Table.Columns {
view.Kanban.Fields = append(view.Kanban.Fields, &av.ViewKanbanField{BaseField: &av.BaseField{ID: col.ID}})
}
case av.LayoutTypeKanban:
for _, field := range firstView.Kanban.Fields {
view.Kanban.Fields = append(view.Kanban.Fields, &av.ViewKanbanField{BaseField: &av.BaseField{ID: field.ID}})
}
}
default:
err = av.ErrWrongLayoutType
@ -2995,7 +3094,7 @@ func setAttributeViewColumnCalc(operation *Operation) (err error) {
break
}
}
case av.LayoutTypeGallery:
case av.LayoutTypeGallery, av.LayoutTypeKanban:
return
}
@ -3145,12 +3244,19 @@ func addAttributeViewBlock(now int64, avID, dbBlockID, viewID, groupID, previous
// The database date field supports filling the current time by default https://github.com/siyuan-note/siyuan/issues/10823
for _, keyValues := range attrView.KeyValues {
if av.KeyTypeDate == keyValues.Key.Type && nil != keyValues.Key.Date && keyValues.Key.Date.AutoFillNow {
if nil == keyValues.GetValue(addingItemID) { // 避免覆盖已有值(可能前面已经通过过滤或者分组条件填充了值)
val := keyValues.GetValue(addingItemID)
if nil == val { // 避免覆盖已有值(可能前面已经通过过滤或者分组条件填充了值)
dateVal := &av.Value{
ID: ast.NewNodeID(), KeyID: keyValues.Key.ID, BlockID: addingItemID, Type: av.KeyTypeDate, IsDetached: isDetached, CreatedAt: now, UpdatedAt: now + 1000,
Date: &av.ValueDate{Content: now, IsNotEmpty: true},
}
keyValues.Values = append(keyValues.Values, dateVal)
} else {
if val.IsRenderAutoFill {
val.CreatedAt, val.UpdatedAt = now, now+1000
val.Date.Content, val.Date.IsNotEmpty, val.Date.IsNotTime = now, true, false
val.IsRenderAutoFill = false
}
}
}
}
@ -3230,11 +3336,13 @@ func fillDefaultValue(attrView *av.AttributeView, view, groupView *av.View, prev
existingVal := keyValues.GetValue(addingItemID)
if nil == existingVal {
newValue.IsRenderAutoFill = false
keyValues.Values = append(keyValues.Values, newValue)
} else {
newValueRaw := newValue.GetValByType(keyValues.Key.Type)
if av.KeyTypeBlock != existingVal.Type || (av.KeyTypeBlock == existingVal.Type && existingVal.IsDetached) {
// 非主键的值直接覆盖,主键的值只覆盖非绑定块
existingVal.IsRenderAutoFill = false
existingVal.SetValByType(keyValues.Key.Type, newValueRaw)
}
}
@ -3486,6 +3594,22 @@ func duplicateAttributeViewKey(operation *Operation) (err error) {
break
}
}
case av.LayoutTypeKanban:
for i, field := range view.Kanban.Fields {
if field.ID == key.ID {
view.Kanban.Fields = append(view.Kanban.Fields[:i+1], append([]*av.ViewKanbanField{
{
BaseField: &av.BaseField{
ID: copyKey.ID,
Wrap: field.Wrap,
Hidden: field.Hidden,
Desc: field.Desc,
},
},
}, view.Kanban.Fields[i+1:]...)...)
break
}
}
}
}
@ -3520,7 +3644,7 @@ func setAttributeViewColWidth(operation *Operation) (err error) {
break
}
}
case av.LayoutTypeGallery:
case av.LayoutTypeGallery, av.LayoutTypeKanban:
return
}
@ -3566,6 +3690,14 @@ func setAttributeViewColWrap(operation *Operation) (err error) {
allFieldWrap = allFieldWrap && field.Wrap
}
view.Gallery.WrapField = allFieldWrap
case av.LayoutTypeKanban:
for _, field := range view.Kanban.Fields {
if field.ID == operation.ID {
field.Wrap = newWrap
}
allFieldWrap = allFieldWrap && field.Wrap
}
view.Kanban.WrapField = allFieldWrap
}
err = av.SaveAttributeView(attrView)
@ -3606,6 +3738,13 @@ func setAttributeViewColHidden(operation *Operation) (err error) {
break
}
}
case av.LayoutTypeKanban:
for _, field := range view.Kanban.Fields {
if field.ID == operation.ID {
field.Hidden = operation.Data.(bool)
break
}
}
}
err = av.SaveAttributeView(attrView)
@ -3639,7 +3778,7 @@ func setAttributeViewColPin(operation *Operation) (err error) {
break
}
}
case av.LayoutTypeGallery:
case av.LayoutTypeGallery, av.LayoutTypeKanban:
return
}
@ -3867,6 +4006,27 @@ func SortAttributeViewViewKey(avID, blockID, keyID, previousKeyID string) (err e
}
}
view.Gallery.CardFields = util.InsertElem(view.Gallery.CardFields, previousIndex, field)
case av.LayoutTypeKanban:
var field *av.ViewKanbanField
for i, kanbanField := range view.Kanban.Fields {
if kanbanField.ID == keyID {
field = kanbanField
curIndex = i
break
}
}
if nil == field {
return
}
view.Kanban.Fields = append(view.Kanban.Fields[:curIndex], view.Kanban.Fields[curIndex+1:]...)
for i, kanbanField := range view.Kanban.Fields {
if kanbanField.ID == previousKeyID {
previousIndex = i + 1
break
}
}
view.Kanban.Fields = util.InsertElem(view.Kanban.Fields, previousIndex, field)
}
err = av.SaveAttributeView(attrView)
@ -3990,8 +4150,8 @@ func AddAttributeViewKey(avID, keyID, keyName, keyType, keyIcon, previousKeyID s
newField.Wrap = view.Table.WrapField
if "" == previousKeyID {
if av.LayoutTypeGallery == currentView.LayoutType {
// 如果当前视图是卡片视图则添加到最后
if av.LayoutTypeGallery == currentView.LayoutType || av.LayoutTypeKanban == currentView.LayoutType {
// 如果当前视图是卡片或看板视图则添加到最后
view.Table.Columns = append(view.Table.Columns, &av.ViewTableColumn{BaseField: newField})
} else {
view.Table.Columns = append([]*av.ViewTableColumn{{BaseField: newField}}, view.Table.Columns...)
@ -4030,6 +4190,26 @@ func AddAttributeViewKey(avID, keyID, keyName, keyType, keyIcon, previousKeyID s
}
}
}
if nil != view.Kanban {
newField.Wrap = view.Kanban.WrapField
if "" == previousKeyID {
view.Kanban.Fields = append(view.Kanban.Fields, &av.ViewKanbanField{BaseField: newField})
} else {
added := false
for i, field := range view.Kanban.Fields {
if field.ID == previousKeyID {
view.Kanban.Fields = append(view.Kanban.Fields[:i+1], append([]*av.ViewKanbanField{{BaseField: newField}}, view.Kanban.Fields[i+1:]...)...)
added = true
break
}
}
if !added {
view.Kanban.Fields = append(view.Kanban.Fields, &av.ViewKanbanField{BaseField: newField})
}
}
}
}
}
@ -4242,6 +4422,13 @@ func RemoveAttributeViewKey(avID, keyID string, removeRelationDest bool) (err er
break
}
}
case av.LayoutTypeKanban:
for i, field := range view.Kanban.Fields {
if field.ID == removedKey.Relation.BackKeyID {
view.Kanban.Fields = append(view.Kanban.Fields[:i], view.Kanban.Fields[i+1:]...)
break
}
}
}
}
}
@ -4286,6 +4473,15 @@ func RemoveAttributeViewKey(avID, keyID string, removeRelationDest bool) (err er
}
}
}
if nil != view.Kanban {
for i, field := range view.Kanban.Fields {
if field.ID == keyID {
view.Kanban.Fields = append(view.Kanban.Fields[:i], view.Kanban.Fields[i+1:]...)
break
}
}
}
}
for _, view := range attrView.Views {

View file

@ -97,14 +97,14 @@ func renderAttributeViewGroups(viewable av.Viewable, attrView *av.AttributeView,
if isGroupByDate(view) {
createdDate := time.UnixMilli(view.GroupCreated).Format("2006-01-02")
if time.Now().Format("2006-01-02") != createdDate {
regenAttrViewGroups(attrView)
genAttrViewGroups(view, attrView) // 仅重新生成一个视图的分组以提升性能
av.SaveAttributeView(attrView)
}
}
// 如果是按模板分组则需要重新生成分组
if isGroupByTemplate(attrView, view) {
regenAttrViewGroups(attrView)
genAttrViewGroups(view, attrView) // 仅重新生成一个视图的分组以提升性能
av.SaveAttributeView(attrView)
}
@ -167,6 +167,8 @@ func renderAttributeViewGroups(viewable av.Viewable, attrView *av.AttributeView,
groupView.Table.Columns = nil
case av.LayoutTypeGallery:
groupView.Gallery.CardFields = nil
case av.LayoutTypeKanban:
groupView.Kanban.Fields = nil
}
}
viewable.SetGroups(groups)
@ -177,7 +179,7 @@ func renderAttributeViewGroups(viewable av.Viewable, attrView *av.AttributeView,
}
func hideEmptyGroupViews(view *av.View, viewable av.Viewable) {
if nil == view.Group {
if !view.IsGroupView() {
return
}
@ -343,14 +345,14 @@ func sortGroupsBySelectOption(view *av.View, groupKey *av.Key) {
}
func isGroupByDate(view *av.View) bool {
if nil == view.Group {
if !view.IsGroupView() {
return false
}
return av.GroupMethodDateDay == view.Group.Method || av.GroupMethodDateWeek == view.Group.Method || av.GroupMethodDateMonth == view.Group.Method || av.GroupMethodDateYear == view.Group.Method || av.GroupMethodDateRelative == view.Group.Method
}
func isGroupByTemplate(attrView *av.AttributeView, view *av.View) bool {
if nil == view.Group {
if !view.IsGroupView() {
return false
}
@ -402,6 +404,19 @@ func renderViewableInstance(viewable av.Viewable, view *av.View, attrView *av.At
end = len(gallery.Cards)
}
gallery.Cards = gallery.Cards[start:end]
case av.LayoutTypeKanban:
kanban := viewable.(*av.Kanban)
kanban.CardCount = 0
kanban.PageSize = view.PageSize
if 1 > pageSize {
pageSize = kanban.PageSize
}
start := (page - 1) * pageSize
end := start + pageSize
if len(kanban.Cards) < end {
end = len(kanban.Cards)
}
kanban.Cards = kanban.Cards[start:end]
}
return
}
@ -486,7 +501,7 @@ func RenderRepoSnapshotAttributeView(indexID, avID string) (viewable av.Viewable
return
}
func RenderHistoryAttributeView(avID, created string) (viewable av.Viewable, attrView *av.AttributeView, err error) {
func RenderHistoryAttributeView(blockID, avID, viewID, query string, page, pageSize int, groupPaging map[string]interface{}, created string) (viewable av.Viewable, attrView *av.AttributeView, err error) {
createdUnix, parseErr := strconv.ParseInt(created, 10, 64)
if nil != parseErr {
logging.LogErrorf("parse created [%s] failed: %s", created, parseErr)
@ -525,6 +540,6 @@ func RenderHistoryAttributeView(avID, created string) (viewable av.Viewable, att
}
}
viewable, err = renderAttributeView(attrView, "", "", "", 1, -1, nil)
viewable, err = renderAttributeView(attrView, blockID, viewID, query, page, pageSize, groupPaging)
return
}

View file

@ -234,6 +234,22 @@ func GetBlockSiblingID(id string) (parent, previous, next string) {
next = flb.ID
}
}
if "" == previous && "" == next && nil != current {
parent = current.ID
if nil != current.Previous {
previous = current.Previous.ID
if flb := treenode.FirstChildBlock(current.Previous); nil != flb {
previous = flb.ID
}
}
if nil != current.Next {
next = current.Next.ID
if flb := treenode.FirstChildBlock(current.Next); nil != flb {
next = flb.ID
}
}
}
return
}

View file

@ -473,6 +473,8 @@ func moveTree(tree *parse.Tree) {
box := Conf.Box(tree.Box)
box.renameSubTrees(tree)
refreshDocInfo(tree)
}
func (box *Box) renameSubTrees(tree *parse.Tree) {

View file

@ -2271,11 +2271,11 @@ func exportTree(tree *parse.Tree, wysiwyg, keepFold, avHiddenCol bool,
switch blockRefMode {
case 2: // 锚文本块链
blockRefLink := &ast.Node{Type: ast.NodeTextMark, TextMarkType: "a", TextMarkTextContent: linkText, TextMarkAHref: "siyuan://blocks/" + defID}
blockRefLink := &ast.Node{Type: ast.NodeTextMark, TextMarkTextContent: linkText, TextMarkAHref: "siyuan://blocks/" + defID}
blockRefLink.KramdownIAL = n.KramdownIAL
if n.IsTextMarkType("inline-memo") {
if "block-ref" != n.TextMarkType { // 除了块引还有其他元素 https://github.com/siyuan-note/siyuan/issues/15698
blockRefLink.TextMarkType = strings.TrimSpace(strings.ReplaceAll(n.TextMarkType, "block-ref", "a"))
blockRefLink.TextMarkInlineMemoContent = n.TextMarkInlineMemoContent
blockRefLink.TextMarkType = "a inline-memo"
}
n.InsertBefore(blockRefLink)
unlinks = append(unlinks, n)
@ -2291,11 +2291,11 @@ func exportTree(tree *parse.Tree, wysiwyg, keepFold, avHiddenCol bool,
}
} else {
blockRefLink = &ast.Node{Type: ast.NodeText, Tokens: []byte(linkText)}
if n.IsTextMarkType("inline-memo") {
if "block-ref" != n.TextMarkType {
blockRefLink.Type = ast.NodeTextMark
blockRefLink.TextMarkInlineMemoContent = n.TextMarkInlineMemoContent
blockRefLink.TextMarkType = "inline-memo"
blockRefLink.TextMarkType = strings.TrimSpace(strings.ReplaceAll(n.TextMarkType, "block-ref", ""))
blockRefLink.TextMarkTextContent = linkText
blockRefLink.TextMarkInlineMemoContent = n.TextMarkInlineMemoContent
}
}
n.InsertBefore(blockRefLink)
@ -2315,13 +2315,13 @@ func exportTree(tree *parse.Tree, wysiwyg, keepFold, avHiddenCol bool,
}
text := &ast.Node{Type: ast.NodeText, Tokens: []byte(linkText)}
n.InsertBefore(text)
if n.IsTextMarkType("inline-memo") {
if "block-ref" != n.TextMarkType {
text.Type = ast.NodeTextMark
text.TextMarkType = "inline-memo"
text.TextMarkType = strings.TrimSpace(strings.ReplaceAll(n.TextMarkType, "block-ref", ""))
text.TextMarkTextContent = linkText
text.TextMarkInlineMemoContent = n.TextMarkInlineMemoContent
}
n.InsertBefore(text)
n.InsertBefore(&ast.Node{Type: ast.NodeFootnotesRef, Tokens: []byte("^" + refFoot.refNum), FootnotesRefId: refFoot.refNum, FootnotesRefLabel: []byte("^" + refFoot.refNum)})
unlinks = append(unlinks, n)
}
@ -3454,6 +3454,11 @@ func getAttrViewTable(attrView *av.AttributeView, view *av.View, query string) (
for _, field := range view.Gallery.CardFields {
view.Table.Columns = append(view.Table.Columns, &av.ViewTableColumn{BaseField: &av.BaseField{ID: field.ID}})
}
case av.LayoutTypeKanban:
view.Table = av.NewLayoutTable()
for _, field := range view.Kanban.Fields {
view.Table.Columns = append(view.Table.Columns, &av.ViewTableColumn{BaseField: &av.BaseField{ID: field.ID}})
}
}
depth := 1

View file

@ -933,7 +933,7 @@ func writeTreeUpsertQueue(tree *parse.Tree) (err error) {
return
}
sql.UpsertTreeQueue(tree)
refreshDocInfo(tree, size)
refreshDocInfoWithSize(tree, size)
return
}
@ -959,7 +959,7 @@ func renameWriteJSONQueue(tree *parse.Tree) (err error) {
}
sql.RenameTreeQueue(tree)
treenode.UpsertBlockTree(tree)
refreshDocInfo(tree, size)
refreshDocInfoWithSize(tree, size)
return
}
@ -1371,6 +1371,8 @@ func moveDoc(fromBox *Box, fromPath string, toBox *Box, toPath string, luteEngin
return
}
fromParentTree := loadParentTree(tree)
moveToRoot := "/" == toPath
toBlockID := tree.ID
fromFolder := path.Join(path.Dir(fromPath), tree.ID)
@ -1489,6 +1491,8 @@ func moveDoc(fromBox *Box, fromPath string, toBox *Box, toPath string, luteEngin
}
evt.Callback = callback
util.PushEvent(evt)
refreshDocInfo(fromParentTree)
return
}

View file

@ -17,7 +17,10 @@
package model
import (
"bytes"
"github.com/88250/lute/ast"
"github.com/88250/lute/editor"
"github.com/88250/lute/render"
"github.com/siyuan-note/logging"
"github.com/siyuan-note/siyuan/kernel/util"
@ -37,13 +40,18 @@ func AutoSpace(rootID string) (err error) {
generateOpTypeHistory(tree, HistoryOpFormat)
luteEngine := NewLute()
// 合并相邻的同类行级节点
ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
if entering {
switch n.Type {
case ast.NodeTextMark:
luteEngine.MergeSameTextMark(n)
}
if !entering {
return ast.WalkContinue
}
switch n.Type {
case ast.NodeTextMark:
luteEngine.MergeSameTextMark(n) // 合并相邻的同类行级节点
case ast.NodeCodeBlockCode:
// 代码块中包含 ``` 时 `优化排版` 异常 `Optimize typography` exception when code block contains ``` https://github.com/siyuan-note/siyuan/issues/15843
n.Tokens = bytes.ReplaceAll(n.Tokens, []byte(editor.Zwj+"```"), []byte("```"))
n.Tokens = bytes.ReplaceAll(n.Tokens, []byte("```"), []byte(editor.Zwj+"```"))
}
return ast.WalkContinue
})

View file

@ -18,7 +18,6 @@ package model
import (
"os"
"path"
"path/filepath"
"strings"
"time"
@ -38,25 +37,22 @@ import (
"github.com/siyuan-note/siyuan/kernel/util"
)
func refreshDocInfo(tree *parse.Tree, size uint64) {
func refreshDocInfo(tree *parse.Tree) {
refreshDocInfoWithSize(tree, filesys.TreeSize(tree))
}
func refreshDocInfoWithSize(tree *parse.Tree, size uint64) {
refreshDocInfo0(tree, size)
refreshParentDocInfo(tree)
}
func refreshParentDocInfo(tree *parse.Tree) {
parentTree := loadParentTree(tree)
if nil == parentTree {
return
}
luteEngine := lute.New()
boxDir := filepath.Join(util.DataDir, tree.Box)
parentDir := path.Dir(tree.Path)
if parentDir == boxDir || parentDir == "/" {
return
}
parentPath := parentDir + ".sy"
parentTree, err := filesys.LoadTree(tree.Box, parentPath, luteEngine)
if err != nil {
return
}
renderer := render.NewJSONRenderer(parentTree, luteEngine.RenderOptions)
data := renderer.Render()
refreshDocInfo0(parentTree, uint64(len(data)))

View file

@ -305,3 +305,16 @@ func indexTreeInFilesystem(rootID string) {
sql.IndexTreeQueue(tree)
logging.LogInfof("reindexed tree by filesystem [rootID=%s]", rootID)
}
func loadParentTree(tree *parse.Tree) (ret *parse.Tree) {
boxDir := filepath.Join(util.DataDir, tree.Box)
parentDir := path.Dir(tree.Path)
if parentDir == boxDir || parentDir == "/" {
return
}
luteEngine := lute.New()
parentPath := parentDir + ".sy"
ret, _ = filesys.LoadTree(tree.Box, parentPath, luteEngine)
return
}

View file

@ -53,6 +53,17 @@ func RenderGroupView(attrView *av.AttributeView, view, groupView *av.View, query
groupView.Gallery.CardSize = view.Gallery.CardSize
groupView.Gallery.FitImage = view.Gallery.FitImage
groupView.Gallery.DisplayFieldName = view.Gallery.DisplayFieldName
case av.LayoutTypeKanban:
err = copier.CopyWithOption(&groupView.Kanban.Fields, &view.Kanban.Fields, copier.Option{DeepCopy: true})
groupView.Kanban.ShowIcon = view.Kanban.ShowIcon
groupView.Kanban.WrapField = view.Kanban.WrapField
groupView.Kanban.CoverFrom = view.Kanban.CoverFrom
groupView.Kanban.CoverFromAssetKeyID = view.Kanban.CoverFromAssetKeyID
groupView.Kanban.CardAspectRatio = view.Kanban.CardAspectRatio
groupView.Kanban.CardSize = view.Kanban.CardSize
groupView.Kanban.FitImage = view.Kanban.FitImage
groupView.Kanban.DisplayFieldName = view.Kanban.DisplayFieldName
}
if nil != err {
logging.LogErrorf("copy view fields [%s] to group [%s] failed: %s", view.ID, groupView.ID, err)
@ -61,6 +72,8 @@ func RenderGroupView(attrView *av.AttributeView, view, groupView *av.View, query
groupView.Table.Columns = view.Table.Columns
case av.LayoutTypeGallery:
groupView.Gallery.CardFields = view.Gallery.CardFields
case av.LayoutTypeKanban:
groupView.Kanban.Fields = view.Kanban.Fields
}
}
@ -91,6 +104,8 @@ func renderView(attrView *av.AttributeView, view *av.View, query string, depth *
ret = RenderAttributeViewTable(attrView, view, query, depth, cachedAttrViews)
case av.LayoutTypeGallery:
ret = RenderAttributeViewGallery(attrView, view, query, depth, cachedAttrViews)
case av.LayoutTypeKanban:
ret = RenderAttributeViewKanban(attrView, view, query, depth, cachedAttrViews)
}
return
}
@ -314,7 +329,7 @@ func filterNotFoundAttrViewItems(keyValuesMap map[string][]*av.KeyValues) {
}
}
func fillAttributeViewBaseValue(baseValue *av.BaseValue, fieldID, itemID string, fieldNumberFormat av.NumberFormat, fieldTemplate string) {
func fillAttributeViewBaseValue(baseValue *av.BaseValue, fieldID, itemID string, fieldNumberFormat av.NumberFormat, fieldTemplate string, fieldDateAutoFill bool) {
switch baseValue.ValueType {
case av.KeyTypeNumber: // 格式化数字
if nil != baseValue.Value && nil != baseValue.Value.Number && baseValue.Value.Number.IsNotEmpty {
@ -330,7 +345,7 @@ func fillAttributeViewBaseValue(baseValue *av.BaseValue, fieldID, itemID string,
}
if nil == baseValue.Value {
baseValue.Value = av.GetAttributeViewDefaultValue(baseValue.ID, fieldID, itemID, baseValue.ValueType)
baseValue.Value = av.GetAttributeViewDefaultValue(baseValue.ID, fieldID, itemID, baseValue.ValueType, fieldDateAutoFill)
} else {
FillAttributeViewNilValue(baseValue.Value, baseValue.ValueType)
}
@ -624,6 +639,7 @@ func fillAttributeViewKeyValues(attrView *av.AttributeView, collection av.Collec
}
}
if !exist {
val.IsRenderAutoFill = true
keyValues.Values = append(keyValues.Values, val)
}
}
@ -772,6 +788,16 @@ func removeMissingField(attrView *av.AttributeView, view *av.View, missingKeyID
}
}
if nil != view.Kanban {
for i, kanbanField := range view.Kanban.Fields {
if kanbanField.ID == missingKeyID {
view.Kanban.Fields = append(view.Kanban.Fields[:i], view.Kanban.Fields[i+1:]...)
changed = true
break
}
}
}
if changed {
av.SaveAttributeView(attrView)
}

View file

@ -108,7 +108,11 @@ func RenderAttributeViewGallery(attrView *av.AttributeView, view *av.View, query
}
galleryCard.ID = cardID
fillAttributeViewBaseValue(fieldValue.BaseValue, field.ID, cardID, field.NumberFormat, field.Template)
filedDateAutoFill := false
if nil != field.Date {
filedDateAutoFill = field.Date.AutoFillNow
}
fillAttributeViewBaseValue(fieldValue.BaseValue, field.ID, cardID, field.NumberFormat, field.Template, filedDateAutoFill)
galleryCard.Values = append(galleryCard.Values, fieldValue)
}

219
kernel/sql/av_kanban.go Normal file
View file

@ -0,0 +1,219 @@
package sql
import (
"fmt"
"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/filesys"
"github.com/siyuan-note/siyuan/kernel/treenode"
"github.com/siyuan-note/siyuan/kernel/util"
)
func RenderAttributeViewKanban(attrView *av.AttributeView, view *av.View, query string, depth *int, cachedAttrViews map[string]*av.AttributeView) (ret *av.Kanban) {
viewable := attrView.RenderedViewables[view.ID]
if nil != viewable {
ret = viewable.(*av.Kanban)
return
}
ret = &av.Kanban{
BaseInstance: av.NewViewBaseInstance(view),
CoverFrom: view.Kanban.CoverFrom,
CoverFromAssetKeyID: view.Kanban.CoverFromAssetKeyID,
CardAspectRatio: view.Kanban.CardAspectRatio,
CardSize: view.Kanban.CardSize,
FitImage: view.Kanban.FitImage,
DisplayFieldName: view.Kanban.DisplayFieldName,
Fields: []*av.KanbanField{},
Cards: []*av.KanbanCard{},
}
// 组装字段
for _, field := range view.Kanban.Fields {
key, getErr := attrView.GetKey(field.ID)
if nil != getErr {
// 找不到字段则在视图中删除
removeMissingField(attrView, view, field.ID)
continue
}
ret.Fields = append(ret.Fields, &av.KanbanField{
BaseInstanceField: &av.BaseInstanceField{
ID: key.ID,
Name: key.Name,
Type: key.Type,
Icon: key.Icon,
Wrap: field.Wrap,
Hidden: field.Hidden,
Desc: key.Desc,
Calc: field.Calc,
Options: key.Options,
NumberFormat: key.NumberFormat,
Template: key.Template,
Relation: key.Relation,
Rollup: key.Rollup,
Date: key.Date,
},
})
}
cardsValues := generateAttrViewItems(attrView, view) // 生成卡片
filterNotFoundAttrViewItems(cardsValues) // 过滤掉不存在的卡片
// 批量加载绑定块对应的树
var ialIDs []string
for _, keyValues := range cardsValues {
for _, kValues := range keyValues {
blockVal := kValues.GetBlockValue()
if nil != blockVal && !blockVal.IsDetached {
ialIDs = append(ialIDs, blockVal.Block.ID)
}
}
}
boundTrees := filesys.LoadTrees(ialIDs)
// 生成卡片字段值
for cardID, cardValues := range cardsValues {
var kanbanCard av.KanbanCard
for _, field := range ret.Fields {
var fieldValue *av.KanbanFieldValue
for _, keyValues := range cardValues {
if keyValues.Key.ID == field.ID {
fieldValue = &av.KanbanFieldValue{
BaseValue: &av.BaseValue{
ID: keyValues.Values[0].ID,
Value: keyValues.Values[0],
ValueType: field.Type,
},
}
break
}
}
if nil == fieldValue {
fieldValue = &av.KanbanFieldValue{
BaseValue: &av.BaseValue{
ID: cardID[:14] + ast.NewNodeID()[14:],
ValueType: field.Type,
},
}
}
kanbanCard.ID = cardID
filedDateAutoFill := false
if nil != field.Date {
filedDateAutoFill = field.Date.AutoFillNow
}
fillAttributeViewBaseValue(fieldValue.BaseValue, field.ID, cardID, field.NumberFormat, field.Template, filedDateAutoFill)
kanbanCard.Values = append(kanbanCard.Values, fieldValue)
}
fillAttributeViewKanbanCardCover(attrView, view, cardValues, &kanbanCard, cardID, luteEngine, boundTrees)
ret.Cards = append(ret.Cards, &kanbanCard)
}
// 回填补全数据
fillAttributeViewKeyValues(attrView, ret)
// 批量获取块属性以提升性能
ials := BatchGetBlockAttrsWitTrees(ialIDs, boundTrees)
// 渲染自动生成的字段值,比如关联、汇总、创建时间和更新时间
fillAttributeViewAutoGeneratedValues(attrView, ret, ials, depth, cachedAttrViews)
// 最后渲染模板字段,这样模板就可以使用汇总、关联、创建时间和更新时间的值了
renderTemplateErr := fillAttributeViewTemplateValues(attrView, view, ret, ials)
if nil != renderTemplateErr {
util.PushErrMsg(fmt.Sprintf(util.Langs[util.Lang][44], util.EscapeHTML(renderTemplateErr.Error())), 30000)
}
filterByQuery(query, ret)
manualSort(view, ret)
return
}
func fillAttributeViewKanbanCardCover(attrView *av.AttributeView, view *av.View, cardValues []*av.KeyValues, kanbanCard *av.KanbanCard, cardID string, luteEngine *lute.Lute, trees map[string]*parse.Tree) {
switch view.Kanban.CoverFrom {
case av.CoverFromNone:
case av.CoverFromContentImage:
blockValue := getBlockValue(cardValues)
if blockValue.IsDetached {
break
}
tree := trees[blockValue.Block.ID]
if nil == tree {
break
}
node := treenode.GetNodeInTree(tree, blockValue.Block.ID)
if nil == node {
break
}
if ast.NodeDocument == node.Type {
if titleImg := treenode.GetDocTitleImgPath(node); "" != titleImg {
kanbanCard.CoverURL = titleImg
break
}
if titleImgCSS := node.IALAttr("title-img"); "" != titleImgCSS {
kanbanCard.CoverURL = titleImgCSS
break
}
}
ast.Walk(node, func(n *ast.Node, entering bool) ast.WalkStatus {
if !entering {
return ast.WalkContinue
}
if ast.NodeImage != n.Type {
return ast.WalkContinue
}
dest := n.ChildByType(ast.NodeLinkDest)
if nil == dest {
return ast.WalkContinue
}
kanbanCard.CoverURL = dest.TokensStr()
return ast.WalkStop
})
if "" == kanbanCard.CoverURL {
kanbanCard.CoverContent = renderCoverContentBlock(node, luteEngine)
return
}
case av.CoverFromAssetField:
if "" == view.Kanban.CoverFromAssetKeyID {
break
}
assetValue := attrView.GetValue(view.Kanban.CoverFromAssetKeyID, cardID)
if nil == assetValue || 1 > len(assetValue.MAsset) {
break
}
p := assetValue.MAsset[0].Content
if util.IsAssetsImage(p) {
kanbanCard.CoverURL = p
}
return
case av.CoverFromContentBlock:
blockValue := getBlockValue(cardValues)
if blockValue.IsDetached {
break
}
tree := trees[blockValue.Block.ID]
if nil == tree {
break
}
node := treenode.GetNodeInTree(tree, blockValue.Block.ID)
if nil == node {
break
}
kanbanCard.CoverContent = renderCoverContentBlock(node, luteEngine)
}
}

View file

@ -96,7 +96,11 @@ func RenderAttributeViewTable(attrView *av.AttributeView, view *av.View, query s
}
tableRow.ID = rowID
fillAttributeViewBaseValue(tableCell.BaseValue, col.ID, rowID, col.NumberFormat, col.Template)
filedDateAutoFill := false
if nil != col.Date {
filedDateAutoFill = col.Date.AutoFillNow
}
fillAttributeViewBaseValue(tableCell.BaseValue, col.ID, rowID, col.NumberFormat, col.Template, filedDateAutoFill)
tableRow.Cells = append(tableRow.Cells, tableCell)
}
ret.Rows = append(ret.Rows, &tableRow)

View file

@ -14,9 +14,9 @@ if errorlevel 1 (
cd ..
echo 'Cleaning Builds'
del /S /Q /F app\build 1>nul
del /S /Q /F app\kernel 1>nul
del /S /Q /F app\kernel-arm64 1>nul
rmdir /S /Q app\build 1>nul
rmdir /S /Q app\kernel 1>nul
rmdir /S /Q app\kernel-arm64 1>nul
echo 'Building Kernel'
@REM the C compiler "gcc" is necessary https://sourceforge.net/projects/mingw-w64/files/mingw-w64/
@ -68,4 +68,13 @@ cd ..
echo 'Building Appx'
echo 'Building Appx should be disabled if you do not need it. Not configured correctly will lead to build failures'
cd . > app\build\win-unpacked\resources\ms-store
electron-windows-store --input-directory app\build\win-unpacked --output-directory app\build\ --package-version 1.0.0.0 --package-name SiYuan --manifest app\appx\AppxManifest.xml --assets app\appx\assets\ --make-pri true
call electron-windows-store --input-directory app\build\win-unpacked --output-directory app\build\ --package-version 1.0.0.0 --package-name SiYuan --manifest app\appx\AppxManifest.xml --assets app\appx\assets\ --make-pri true
rmdir /S /Q app\build\pre-appx 1>nul
echo 'Building Appx arm64'
echo 'Building Appx arm64 should be disabled if you do not need it. Not configured correctly will lead to build failures'
cd . > app\build\win-arm64-unpacked\resources\ms-store
call electron-windows-store --input-directory app\build\win-arm64-unpacked --output-directory app\build\ --package-version 1.0.0.0 --package-name SiYuan-arm64 --manifest app\appx\AppxManifest-arm64.xml --assets app\appx\assets\ --make-pri true
rmdir /S /Q app\build\pre-appx 1>nul