Compare commits

..

87 commits

Author SHA1 Message Date
Daniel
f1c91863b9
🔖 Release v3.3.2
Some checks failed
Release Docker Image / build (push) Has been cancelled
Signed-off-by: Daniel <845765@qq.com>
2025-09-09 18:15:27 +08:00
Daniel
8a9e746891
🎨 Clean code
Signed-off-by: Daniel <845765@qq.com>
2025-09-09 18:11:49 +08:00
Jeffrey Chen
517f5c8453
🎨 Hide the separator line in the top bar plugin menu in publish service (#15809) 2025-09-09 18:08:21 +08:00
Vanessa
88431279bf 🎨 https://github.com/siyuan-note/siyuan/issues/15806 2025-09-09 17:32:35 +08:00
Vanessa
dc656d83a2 🎨 https://github.com/siyuan-note/siyuan/issues/15806 2025-09-09 16:43:39 +08:00
Vanessa
b69fd04137 🎨 https://github.com/siyuan-note/siyuan/issues/15806 2025-09-09 10:18:44 +08:00
Vanessa
ff909fa149 🎨 https://github.com/siyuan-note/siyuan/issues/15805 2025-09-09 10:02:51 +08:00
Vanessa
bfe50d9009 🚨 2025-09-09 09:29:00 +08:00
Daniel
93422c134d
🎨 Add field disabledInPublish to the code snippet to indicate whether it is disabled in the publish service https://github.com/siyuan-note/siyuan/issues/15806
Signed-off-by: Daniel <845765@qq.com>
2025-09-09 09:18:12 +08:00
Daniel
2f70ef43a1
🎨 Clean code
Signed-off-by: Daniel <845765@qq.com>
2025-09-09 09:07:23 +08:00
Daniel
5f6ddb4655
📝 Update changelogs
Signed-off-by: Daniel <845765@qq.com>
2025-09-08 19:41:02 +08:00
Daniel
39c4b3325e
🧑‍💻 Improve kernel API appendBlock, insertBlock and prependBlock https://github.com/siyuan-note/siyuan/issues/15798
Signed-off-by: Daniel <845765@qq.com>
2025-09-08 19:24:41 +08:00
Vanessa
393c53941a
🎨 https://github.com/siyuan-note/siyuan/issues/15801
Signed-off-by: Daniel <845765@qq.com>
2025-09-08 17:39:03 +08:00
Daniel
a4f03191fa
🎨 https://github.com/siyuan-note/siyuan/issues/15777 2025-09-08 10:58:58 +08:00
Vanessa
de9e648e9e 🎨 https://github.com/siyuan-note/siyuan/issues/15782 2025-09-08 10:52:40 +08:00
Vanessa
c1f14a33d9 🎨 https://github.com/siyuan-note/siyuan/pull/15731 2025-09-08 10:06:52 +08:00
Jeffrey Chen
adca241ed5
🎨 Improve slash menu (#15731)
fix https://github.com/siyuan-note/siyuan/issues/12518
2025-09-08 09:57:36 +08:00
Vanessa
006da6bc90 🎨 https://github.com/siyuan-note/siyuan/issues/15782 2025-09-08 09:48:36 +08:00
Vanessa
490234caab 🚨 2025-09-08 09:44:52 +08:00
Daniel
80c357564d
🎨 Regen pnpm-lock.yaml 2025-09-07 21:47:41 +08:00
Daniel
6852a49620
🎨 Improve build script 2025-09-07 21:40:17 +08:00
Daniel
93591ad44e
🐛 Fix NPE https://github.com/siyuan-note/siyuan/issues/15796 2025-09-07 21:05:54 +08:00
Daniel
6510d7dbf0
🧑‍💻 Improve kernel API appendBlock and insertBlock https://github.com/siyuan-note/siyuan/issues/15798 2025-09-07 20:58:38 +08:00
Daniel
29244a1f8c
🧑‍💻 https://github.com/siyuan-note/siyuan/issues/15798 2025-09-07 19:20:44 +08:00
Daniel
a85467751f
⬆️ Upgrade lute 2025-09-07 19:02:52 +08:00
Vanessa
14251d8dae 🎨 https://github.com/siyuan-note/siyuan/pull/15772 2025-09-07 18:40:12 +08:00
Jeffrey Chen
52a4815419
🐛 Improve database date field (#15772)
fix https://github.com/siyuan-note/siyuan/issues/13252 , https://github.com/siyuan-note/siyuan/issues/15747
2025-09-07 18:37:33 +08:00
Vanessa
45a6a190d0 🎨 https://github.com/siyuan-note/siyuan/issues/15185 2025-09-07 18:14:46 +08:00
Vanessa
c1a4aa3128 🐛 https://github.com/siyuan-note/siyuan/issues/15791 2025-09-07 11:01:11 +08:00
Vanessa
0fce405a05 Merge remote-tracking branch 'origin/dev' into dev 2025-09-07 09:59:48 +08:00
Vanessa
fe143bcb12 🎨 https://github.com/siyuan-note/siyuan/issues/8019 2025-09-07 09:59:35 +08:00
Daniel
0ad7c4cf23
🎨 Improve open the user guide https://github.com/siyuan-note/siyuan/issues/15792 2025-09-07 09:35:48 +08:00
Vanessa
c326989391 🎨 https://github.com/siyuan-note/siyuan/issues/8019 2025-09-06 22:47:16 +08:00
Vanessa
468b670bcc 🎨 https://github.com/siyuan-note/siyuan/issues/8019 2025-09-06 22:33:31 +08:00
Vanessa
4e4398ef47 🎨 https://github.com/siyuan-note/siyuan/issues/8019 2025-09-06 18:31:38 +08:00
Vanessa
fc1cbf46aa Merge remote-tracking branch 'origin/dev' into dev 2025-09-06 18:02:31 +08:00
Vanessa
80cccd41b5 🎨 https://github.com/siyuan-note/siyuan/issues/8019 2025-09-06 18:02:18 +08:00
Daniel
68991a6aef
🎨 Copy/Cut folded heading changed to copy/cut Headings and Bottom Blocks and support multiple headings copy/cut https://github.com/siyuan-note/siyuan/issues/8019 2025-09-06 17:30:35 +08:00
Daniel
2a8b47b518
🎨 Copy/Cut folded heading changed to copy/cut Headings and Bottom Blocks and support multiple headings copy/cut https://github.com/siyuan-note/siyuan/issues/8019 2025-09-06 17:15:42 +08:00
Daniel
8ad3cb00ad
🎨 Copy/Cut folded heading changed to copy/cut Headings and Bottom Blocks and support multiple headings copy/cut https://github.com/siyuan-note/siyuan/issues/8019 2025-09-06 17:11:21 +08:00
Daniel
c55c413365
🎨 Copy/Cut folded heading changed to copy/cut Headings and Bottom Blocks and support multiple headings copy/cut https://github.com/siyuan-note/siyuan/issues/8019 2025-09-06 17:10:43 +08:00
Vanessa
4767e399e2 Merge remote-tracking branch 'origin/dev' into dev 2025-09-06 10:22:29 +08:00
Vanessa
5ade06a3b6 🎨 https://github.com/siyuan-note/siyuan/issues/15609 2025-09-06 10:22:16 +08:00
Daniel
8ebb617072
📝 Database rollup field filtering rules support "Any", "All", and "None" https://github.com/siyuan-note/siyuan/issues/15609 2025-09-06 08:43:59 +08:00
Daniel
305bd9dfb0
🎨 Improve duplicating doc/av https://github.com/siyuan-note/siyuan/issues/15786 2025-09-06 08:28:54 +08:00
Vanessa
d2f990a830 🎨 https://github.com/siyuan-note/siyuan/issues/15609 2025-09-05 23:44:46 +08:00
Vanessa
5775aa9734 Merge remote-tracking branch 'origin/dev' into dev 2025-09-05 23:38:13 +08:00
Vanessa
80f1c6c3ec 🎨 https://github.com/siyuan-note/siyuan/issues/15609 2025-09-05 23:37:58 +08:00
Daniel
f220e3627f
📝 Database rollup field filtering rules support "Any", "All", and "None" https://github.com/siyuan-note/siyuan/issues/15609 2025-09-05 23:15:20 +08:00
Daniel
1fb4ed980b
⬆️ Upgrade lute 2025-09-05 22:51:41 +08:00
Daniel
04bf9e9848
🎨 Improve av https://github.com/siyuan-note/siyuan/issues/15775 2025-09-05 17:44:03 +08:00
Daniel
9447f1c5a8
🎨 Clean code 2025-09-05 17:38:46 +08:00
Vanessa
afa74ebb4b 🎨 https://github.com/siyuan-note/siyuan/issues/15771 2025-09-05 17:16:48 +08:00
Jeffrey Chen
17a96ee1b1
🎨 Dragging multiple files into the editor will cause them to be opened by the default program (#15773) 2025-09-05 12:10:44 +08:00
Vanessa
647204eee1 🎨 https://github.com/siyuan-note/siyuan/issues/15733 2025-09-05 12:01:46 +08:00
Vanessa
4eb91f2e40 Merge remote-tracking branch 'origin/dev' into dev 2025-09-05 11:26:31 +08:00
Vanessa
d48e3d5211 🎨 https://github.com/siyuan-note/siyuan/issues/15771 2025-09-05 11:26:18 +08:00
Jeffrey Chen
59265bfc94
📝 Improve text (#15774) 2025-09-05 10:27:07 +08:00
Daniel
16913fb065
⬆️ Upgrade pnpm 2025-09-05 10:16:25 +08:00
Vanessa
65b52a2bc6 🎨 https://github.com/siyuan-note/siyuan/issues/15771 2025-09-05 08:59:52 +08:00
Vanessa
ac6b1d6689 🎨 https://github.com/siyuan-note/siyuan/issues/15768 2025-09-04 21:45:25 +08:00
Vanessa
7ae90058c3 🎨 https://github.com/siyuan-note/siyuan/issues/15771 2025-09-04 20:48:40 +08:00
Daniel
fdfd453e2b
🎨 https://github.com/siyuan-note/siyuan/issues/15748 2025-09-04 19:53:53 +08:00
Vanessa
0aa5624495 Merge remote-tracking branch 'origin/dev' into dev 2025-09-04 19:48:31 +08:00
Vanessa
0b3cef8964 🎨 https://github.com/siyuan-note/siyuan/issues/15748 2025-09-04 19:48:18 +08:00
Daniel
2c5d4d47a4
🎨 https://github.com/siyuan-note/siyuan/issues/15749 2025-09-04 19:38:49 +08:00
Vanessa
3379d29e67 Merge remote-tracking branch 'origin/dev' into dev 2025-09-04 18:24:04 +08:00
Vanessa
ba95f45b92 🎨 https://github.com/siyuan-note/siyuan/issues/15751 2025-09-04 18:23:51 +08:00
Daniel
ed78e67af2
🎨 Clean code 2025-09-04 18:08:45 +08:00
Daniel
ba10d96a10
Revert "🎨 Add tags to the database when binding blocks to the database https://github.com/siyuan-note/siyuan/issues/15757"
This reverts commit 4d8bc1672c.
2025-09-04 18:00:47 +08:00
Daniel
4d8bc1672c
🎨 Add tags to the database when binding blocks to the database https://github.com/siyuan-note/siyuan/issues/15757 2025-09-04 17:43:18 +08:00
Vanessa
e91a37a98b 🎨 https://github.com/siyuan-note/siyuan/issues/15760 2025-09-04 16:38:07 +08:00
Vanessa
f6bd240a85 Merge remote-tracking branch 'origin/dev' into dev 2025-09-04 16:31:24 +08:00
Vanessa
c08f88156f 🎨 https://github.com/siyuan-note/siyuan/issues/15750 2025-09-04 16:31:12 +08:00
Daniel
74826fc0ce
🎨 Improve database performance https://github.com/siyuan-note/siyuan/issues/15764 2025-09-04 16:17:39 +08:00
Daniel
e1bb9874be
🎨 Clean code 2025-09-04 16:15:03 +08:00
Daniel
2b85cb1b6c
🎨 Improve database performance https://github.com/siyuan-note/siyuan/issues/15764 2025-09-04 16:14:38 +08:00
Daniel
cd8c3a41e6
🎨 Clean code 2025-09-04 15:40:23 +08:00
Jeffrey Chen
bdace41d97
📝 Improve User Guide (#15767)
https://github.com/siyuan-note/siyuan/issues/14430
2025-09-04 15:40:06 +08:00
Vanessa
ea2294e901 🎨 https://github.com/siyuan-note/siyuan/issues/15761 2025-09-04 12:11:22 +08:00
Vanessa
868c9d980a Merge remote-tracking branch 'origin/dev' into dev 2025-09-04 10:30:18 +08:00
Vanessa
2d53c81026 Merge remote-tracking branch 'origin/dev' into dev 2025-09-04 10:30:06 +08:00
Daniel
6ff4439be3
🧑‍💻 Add field disabledInPublish to the marketplace package metadata to indicate whether it is disabled in the publishing service https://github.com/siyuan-note/siyuan/issues/11730 2025-09-04 10:29:58 +08:00
Vanessa
d47a8ffe02 🎨 https://github.com/siyuan-note/siyuan/issues/15639 2025-09-04 10:29:54 +08:00
Daniel
9410a70a2b
🎨 Add marketplace package config item minAppVersion https://github.com/siyuan-note/siyuan/issues/8330 2025-09-04 10:22:57 +08:00
Daniel
3c3f34442f
🎨 Improve bazaar package 2025-09-04 10:14:41 +08:00
Daniel
9dc6f56bff
🎨 Improve bazaar package 2025-09-04 10:14:21 +08:00
83 changed files with 4414 additions and 3191 deletions

View file

@ -7,7 +7,7 @@
## NPM dependencies
Install pnpm: `npm install -g pnpm@10.15.0`
Install pnpm: `npm install -g pnpm@10.15.1`
<details>
<summary>For China mainland</summary>

View file

@ -7,7 +7,7 @@
## NPM 依赖
安装 pnpm`npm install -g pnpm@10.15.0`
安装 pnpm`npm install -g pnpm@10.15.1`
<details>
<summary>适用于中国大陆</summary>

View file

@ -54,7 +54,7 @@
"dataRepoAutoPurgeRetentionIndexesDaily": "عدد لقطات البيانات في اليوم",
"fields": "حقول",
"dynamicIcon": "أيقونة ديناميكية",
"dynamicIconDateEmptyInfo": "إذا كان التاريخ فارغًا، فسيظهر أيقونة التقويم التاريخ الحالي بشكل ديناميكي",
"dynamicIconDateEmptyInfo": "مسح التاريخ، سيعرض رمز التقويم تاريخ اليوم ديناميكيًا",
"backlinkContainChildren": "جعل الروابط المرجعية تحتوي على كتل فرعية",
"backlinkContainChildrenTip": "عند التمكين، سيتم تضمين الكتل الفرعية في حساب الروابط المرجعية",
"entryNum": "عدد المدخلات",
@ -340,6 +340,9 @@
"filterOperatorIsAfter": "بعد",
"filterOperatorIsOnOrBefore": "في أو قبل",
"filterOperatorIsOnOrAfter": "في أو بعد",
"filterQuantifierAny": "أي",
"filterQuantifierAll": "الكل",
"filterQuantifierNone": "لا شيء",
"asc": "تصاعدي",
"desc": "تنازلي",
"hideCol": "إخفاء الحقل",
@ -1121,7 +1124,7 @@
"fileTree3": "لا يتطلب تأكيد عند حذف المستندات",
"fileTree4": "في حالة عدم التمكين، سوف يظهر مربع التأكيد في كل مرة تقوم فيها بحذف مستند",
"fileTree5": "موقع حفظ المستند الجديد المنشَأ من المرجع",
"fileTree6": "‫عند استخدام <code class='fn__code'>((</code>، مسار حفظ المستند الجديد (على سبيل المثال، <code class='fn__code'>/folder1/{{now | date \"20060102150405\"}}/</code>)",
"fileTree6": "‫عند استخدام <code class='fn__code'>((</code> أو <code class='fn__code'>[[</code>، مسار حفظ المستند الجديد (على سبيل المثال، <code class='fn__code'>/folder1/{{now | date \"20060102150405\"}}/</code>)",
"fileTree7": "فتح في علامة التبويب الحالية",
"fileTree8": "سيتم استبدال علامة التبويب المستند المفتوحة حديثا علامة التبويب غير المعدلة",
"fileTree9": "إغلاق جميع علامات التبويب عند بدء التشغيل",

View file

@ -54,7 +54,7 @@
"dataRepoAutoPurgeRetentionIndexesDaily": "Daten-Snapshots pro Tag",
"fields": "Attribut",
"dynamicIcon": "Dynamisches Icon",
"dynamicIconDateEmptyInfo": "Wenn das Datum leer ist, zeigt das Kalendersymbol dynamisch das heutige Datum an",
"dynamicIconDateEmptyInfo": "Datum löschen, das Kalender-Symbol zeigt dynamisch das heutige Datum an",
"backlinkContainChildren": "Enthalten Rückverweise untergeordnete Blöcke",
"backlinkContainChildrenTip": "Wenn aktiviert, werden untergeordnete Blöcke in die Berechnung der Rückverweise einbezogen",
"entryNum": "Anzahl der Einträge",
@ -340,6 +340,9 @@
"filterOperatorIsAfter": "Ist nach",
"filterOperatorIsOnOrBefore": "Ist am oder vor",
"filterOperatorIsOnOrAfter": "Ist am oder nach",
"filterQuantifierAny": "Beliebig",
"filterQuantifierAll": "Alle",
"filterQuantifierNone": "Keine",
"asc": "Aufsteigend",
"desc": "Absteigend",
"hideCol": "Spalte ausblenden",
@ -1121,7 +1124,7 @@
"fileTree3": "Keine Bestätigung erforderlich beim Löschen von Dokumenten",
"fileTree4": "Wenn nicht aktiviert, wird jedes Mal ein Bestätigungsfeld angezeigt, wenn Sie ein Dokument löschen",
"fileTree5": "Referenz erstellt Doc-Speicherort",
"fileTree6": "Beim Verwenden von <code class='fn__code'>((</code> der Speicherpfad des neuen Dokuments (z.B. <code class='fn__code'>/folder1/{{now | date \"20060102150405\"}}/</code>)",
"fileTree6": "Beim Verwenden von <code class='fn__code'>((</code> oder <code class='fn__code'>[[</code> der Speicherpfad des neuen Dokuments (z.B. <code class='fn__code'>/folder1/{{now | date \"20060102150405\"}}/</code>)",
"fileTree7": "Im aktuellen Tab öffnen",
"fileTree8": "Der neu geöffnete Dokumenten-Tab ersetzt den nicht modifizierten Tab",
"fileTree9": "Alle Tabs beim Start schließen",

View file

@ -54,7 +54,7 @@
"dataRepoAutoPurgeRetentionIndexesDaily": "Data snapshots per day",
"fields": "Fields",
"dynamicIcon": "Dynamic icon",
"dynamicIconDateEmptyInfo": "When date is empty, the calendar icon will show today's date dynamically",
"dynamicIconDateEmptyInfo": "Clear the date, the calendar icon will dynamically display todays date",
"backlinkContainChildren": "Do backlinks contain child blocks",
"backlinkContainChildrenTip": "When enabled, child blocks will be included in the backlink calculation",
"entryNum": "Number of entries",
@ -340,6 +340,9 @@
"filterOperatorIsAfter": "Is after",
"filterOperatorIsOnOrBefore": "Is on or before",
"filterOperatorIsOnOrAfter": "Is on or after",
"filterQuantifierAny": "Any",
"filterQuantifierAll": "All",
"filterQuantifierNone": "None",
"asc": "Ascending",
"desc": "Descending",
"hideCol": "Hide field",
@ -1121,7 +1124,7 @@
"fileTree3": "No confirmation required when deleting documents",
"fileTree4": "If not enabled, a confirmation box will pop up every time you delete a document",
"fileTree5": "Ref create doc save location",
"fileTree6": "When using <code class='fn__code'>((</code>, the save path of the new document (for example, <code class='fn__code'>/folder1/{{now | date \"20060102150405\"}}/</code>)",
"fileTree6": "When using <code class='fn__code'>((</code> or <code class='fn__code'>[[</code>, the save path of the new document (for example, <code class='fn__code'>/folder1/{{now | date \"20060102150405\"}}/</code>)",
"fileTree7": "Open in the current tab",
"fileTree8": "The newly opened document tab will replace the unmodified tab",
"fileTree9": "Close all tabs at startup",

View file

@ -54,7 +54,7 @@
"dataRepoAutoPurgeRetentionIndexesDaily": "Número de instantáneas de datos por día",
"fields": "Atributo",
"dynamicIcon": "Icono dinámico",
"dynamicIconDateEmptyInfo": "Si la fecha está vacía, el icono del calendario mostrará dinámicamente la fecha de hoy",
"dynamicIconDateEmptyInfo": "Borrar la fecha, el icono del calendario mostrará dinámicamente la fecha actual",
"backlinkContainChildren": "¿Los enlaces inversos contienen bloques secundarios?",
"backlinkContainChildrenTip": "Una vez habilitado, los bloques secundarios se incluirán en el cálculo de los enlaces inversos",
"entryNum": "Número de entradas",
@ -340,6 +340,9 @@
"filterOperatorIsAfter": "Es posterior a",
"filterOperatorIsOnOrBefore": "Está activado o antes",
"filterOperatorIsOnOrAfter": "Está encendido o después",
"filterQuantifierAny": "Cualquiera",
"filterQuantifierAll": "Todos",
"filterQuantifierNone": "Ninguno",
"asc": "Ascendente",
"desc": "Descendente",
"hideCol": "Ocultar columna",
@ -1121,7 +1124,7 @@
"fileTree3": "No se requiere confirmación al borrar documentos",
"fileTree4": "Si no se activa, aparecerá un cuadro de confirmación cada vez que se elimine un documento",
"fileTree5": "Ref crear ubicación de guardado de documentos",
"fileTree6": "Al utilizar <code class='fn__code'>((</code>, la ruta de guardado del nuevo documento (por ejemplo, <code class='fn__code'>/carpeta1/{{now | date \"20060102150405\"}}/</code>)",
"fileTree6": "Al utilizar <code class='fn__code'>((</code> o <code class='fn__code'>[[</code>, la ruta de guardado del nuevo documento (por ejemplo, <code class='fn__code'>/carpeta1/{{now | date \"20060102150405\"}}/</code>)",
"fileTree7": "Abrir en la pestaña actual",
"fileTree8": "La pestaña del documento recién abierto sustituirá a la pestaña no modificada",
"fileTree9": "Cerrar todas las pestañas al inicio",

View file

@ -54,7 +54,7 @@
"dataRepoAutoPurgeRetentionIndexesDaily": "Nombre d'instantanés de données par jour",
"fields": "Attribut",
"dynamicIcon": "Icône dynamique",
"dynamicIconDateEmptyInfo": "Si la date est vide, l'icône du calendrier affichera dynamiquement la date d'aujourd'hui",
"dynamicIconDateEmptyInfo": "Effacer la date, licône du calendrier affichera dynamiquement la date du jour",
"backlinkContainChildren": "Les liens retour contiennent-ils des sous-blocs",
"backlinkContainChildrenTip": "Une fois activé, les sous-blocs seront inclus dans le calcul des liens retour",
"entryNum": "Nombre d'entrées",
@ -340,6 +340,9 @@
"filterOperatorIsAfter": "Est après",
"filterOperatorIsOnOrBefore": "Est le ou avant",
"filterOperatorIsOnOrAfter": "Est allumé ou après",
"filterQuantifierAny": "N'importe lequel",
"filterQuantifierAll": "Tous",
"filterQuantifierNone": "Aucun",
"asc": "Ascendant",
"desc": "Descendant",
"hideCol": "Masquer la colonne",
@ -1121,7 +1124,7 @@
"fileTree3": "Aucune confirmation requise lors de la suppression de documents",
"fileTree4": "Si non activé, une boîte de confirmation apparaîtra à chaque fois que vous supprimerez un document",
"fileTree5": "Référence créer doc enregistrer emplacement",
"fileTree6": "Lors de l'utilisation de <code class='fn__code'>((</code>, le chemin d'enregistrement du nouveau document (par exemple, <code class='fn__code'>/dossier1/{{now | date \"20060102150405\"}}/</code>).",
"fileTree6": "Lors de l'utilisation de <code class='fn__code'>((</code> ou <code class='fn__code'>[[</code>, le chemin d'enregistrement du nouveau document (par exemple, <code class='fn__code'>/dossier1/{{now | date \"20060102150405\"}}/</code>).",
"fileTree7": "Ouvrir dans l'Onglet actuel",
"fileTree8": "L'onglet du document nouvellement ouvert remplacera l'Onglet non modifié.",
"fileTree9": "Fermer tous les onglets au démarrage",

View file

@ -54,7 +54,7 @@
"dataRepoAutoPurgeRetentionIndexesDaily": "מספר תמונות נתונים ביום",
"fields": "מאפיין",
"dynamicIcon": "אייקון דינמי",
"dynamicIconDateEmptyInfo": "אם התאריך ריק, סמל הלוח השנה יציג באופן דינמי את התאריך של היום",
"dynamicIconDateEmptyInfo": "נקה את התאריך, סמל לוח השנה יציג באופן דינמי את התאריך של היום",
"backlinkContainChildren": "האם קישורים חוזרים כוללים בלוקים משניים",
"backlinkContainChildrenTip": "לאחר ההפעלה, בלוקים משניים ייכללו בחישוב הקישורים החוזרים",
"entryNum": "מספר ערכים",
@ -340,6 +340,9 @@
"filterOperatorIsAfter": "היה אחרי",
"filterOperatorIsOnOrBefore": "היה על או לפני",
"filterOperatorIsOnOrAfter": "היה על או אחרי",
"filterQuantifierAny": "כל אחד",
"filterQuantifierAll": "הכל",
"filterQuantifierNone": "אין",
"asc": "עולה",
"desc": "יורד",
"hideCol": "החבא עמודה",
@ -1121,7 +1124,7 @@
"fileTree3": "אין צורך באישור בעת מחיקת מסמכים",
"fileTree4": "אם לא הופק, תופיע תיבת אישור בכל פעם שמחקים מסמך",
"fileTree5": "מיקום שמירת מסמך להפניה",
"fileTree6": "בעת השימוש <code class='fn__code'>((</code>, מסלול השמירה של המסמך החדש (למשל, <code class='fn__code'>/folder1/{{now | date \"20060102150405\"}}</code>)",
"fileTree6": "בעת השימוש <code class='fn__code'>((</code> או <code class='fn__code'>[[</code>, מסלול השמירה של המסמך החדש (למשל, <code class='fn__code'>/folder1/{{now | date \"20060102150405\"}}</code>)",
"fileTree7": "פתח בטאב הנוכחי",
"fileTree8": "טאב המסמך הנפתח החדש יחליף את הטאב הלא משתנה",
"fileTree9": "סגור את כל הטאבים בעת אתחול",

View file

@ -54,7 +54,7 @@
"dataRepoAutoPurgeRetentionIndexesDaily": "Numero di snapshot dei dati al giorno",
"fields": "Campi",
"dynamicIcon": "Emoji dinamica",
"dynamicIconDateEmptyInfo": "Se la data è vuota, l'icona del calendario mostrerà dinamicamente la data odierna",
"dynamicIconDateEmptyInfo": "Cancella la data, l'icona del calendario mostrerà dinamicamente la data odierna",
"backlinkContainChildren": "I backlink contengono blocchi figli",
"backlinkContainChildrenTip": "Dopo l'attivazione, i blocchi figli saranno inclusi nel calcolo dei backlink",
"entryNum": "Numero di voci",
@ -340,6 +340,9 @@
"filterOperatorIsAfter": "È dopo",
"filterOperatorIsOnOrBefore": "È il giorno o prima di",
"filterOperatorIsOnOrAfter": "È il giorno o dopo di",
"filterQuantifierAny": "Qualsiasi",
"filterQuantifierAll": "Tutti",
"filterQuantifierNone": "Nessuno",
"asc": "Ascendente",
"desc": "Discendente",
"hideCol": "Nascondi campo",
@ -1121,7 +1124,7 @@
"fileTree3": "Nessuna conferma richiesta durante l'eliminazione dei documenti",
"fileTree4": "Se non abilitato, ogni volta che elimini un documento verrà visualizzata una finestra di conferma",
"fileTree5": "Percorso di salvataggio dei nuovi documenti creati con riferimento",
"fileTree6": "Quando usi <code class='fn__code'>((</code>, il percorso di salvataggio del nuovo documento (ad esempio, <code class='fn__code'>/folder1/{{now | date \"20060102150405\"}}/</code>)",
"fileTree6": "Quando usi <code class='fn__code'>((</code> o <code class='fn__code'>[[</code>, il percorso di salvataggio del nuovo documento (ad esempio, <code class='fn__code'>/folder1/{{now | date \"20060102150405\"}}/</code>)",
"fileTree7": "Apri nella scheda corrente",
"fileTree8": "La scheda del documento appena aperta sostituirà la scheda non modificata",
"fileTree9": "Chiudi tutte le schede all'avvio",

View file

@ -54,7 +54,7 @@
"dataRepoAutoPurgeRetentionIndexesDaily": "データスナップショットの毎日の保持数",
"fields": "属性",
"dynamicIcon": "動的アイコン",
"dynamicIconDateEmptyInfo": "日付が空の場合、カレンダーアイコンは現在の日付を動的に表示します",
"dynamicIconDateEmptyInfo": "日付をクリアすると、カレンダーアイコンが当日の日付を動的に表示します",
"backlinkContainChildren": "バックリンクに子ブロックを含めるかどうか",
"backlinkContainChildrenTip": "有効にすると、子ブロックがバックリンク計算に含まれます",
"entryNum": "エントリ数",
@ -340,6 +340,9 @@
"filterOperatorIsAfter": "より後",
"filterOperatorIsOnOrBefore": "以前",
"filterOperatorIsOnOrAfter": "以降",
"filterQuantifierAny": "いずれか",
"filterQuantifierAll": "すべて",
"filterQuantifierNone": "なし",
"asc": "昇順",
"desc": "降順",
"hideCol": "列を非表示",
@ -1121,7 +1124,7 @@
"fileTree3": "ドキュメントを削除するときに確認しない",
"fileTree4": "ドキュメントを削除するときに確認ボックスを表示しません",
"fileTree5": "参照の保存場所",
"fileTree6": "<code class='fn__code'>((</code> を使用した時の参照の保存パス (例: <code class='fn__code'>/folder1/{{now | date \"20060102150405\"}}/</code>)",
"fileTree6": "<code class='fn__code'>((</code> または <code class='fn__code'>[[</code> を使用した時の参照の保存パス (例: <code class='fn__code'>/folder1/{{now | date \"20060102150405\"}}/</code>)",
"fileTree7": "現在のタブで開く",
"fileTree8": "新しく開いたドキュメントのタブが未変更のタブを置き換えます",
"fileTree9": "起動時にすべてのタブを閉じる",

View file

@ -54,7 +54,7 @@
"dataRepoAutoPurgeRetentionIndexesDaily": "Liczba migawek danych dziennie",
"fields": "Atrybut",
"dynamicIcon": "Ikona dynamiczna",
"dynamicIconDateEmptyInfo": "Jeśli data jest pusta, ikona kalendarza będzie dynamicznie pokazywać dzisiejszą datę",
"dynamicIconDateEmptyInfo": "Wyczyść datę, ikona kalendarza będzie dynamicznie wyświetlać bieżącą datę",
"backlinkContainChildren": "Czy linki zwrotne zawierają bloki podrzędne",
"backlinkContainChildrenTip": "Po włączeniu bloki podrzędne zostaną uwzględnione w obliczeniach linków zwrotnych",
"entryNum": "Количество записей",
@ -340,6 +340,9 @@
"filterOperatorIsAfter": "Jest po",
"filterOperatorIsOnOrBefore": "Jest na lub przed",
"filterOperatorIsOnOrAfter": "Jest na lub po",
"filterQuantifierAny": "Dowolny",
"filterQuantifierAll": "Wszystkie",
"filterQuantifierNone": "Brak",
"asc": "Rosnąco",
"desc": "Malejąco",
"hideCol": "Ukryj kolumnę",
@ -1121,7 +1124,7 @@
"fileTree3": "Nie wymaga potwierdzenia przy usuwaniu dokumentów",
"fileTree4": "Jeśli nie jest włączone, okno potwierdzenia pojawi się za każdym razem, gdy usuniesz dokument",
"fileTree5": "Ref stworzone miejsce zapisu dokumentu",
"fileTree6": "Kiedy używasz <code class='fn__code'>((</code>, ścieżka zapisu nowego dokumentu (na przykład <code class='fn__code'>/folder1/{{now | date \"20060102150405\"}}/</code>)",
"fileTree6": "Kiedy używasz <code class='fn__code'>((</code> lub <code class='fn__code'>[[</code>, ścieżka zapisu nowego dokumentu (na przykład <code class='fn__code'>/folder1/{{now | date \"20060102150405\"}}/</code>)",
"fileTree7": "Otwórz w bieżącej zakładce",
"fileTree8": "Nowa otwarta zakładka dokumentu zastąpi niezmodyfikowaną zakładkę",
"fileTree9": "Zamknij wszystkie zakładki przy uruchamianiu",

View file

@ -54,7 +54,7 @@
"dataRepoAutoPurgeRetentionIndexesDaily": "Instantâneos de dados por dia",
"fields": "Campos",
"dynamicIcon": "Ícone dinâmico",
"dynamicIconDateEmptyInfo": "Se a data estiver vazia, o ícone do calendário exibirá dinamicamente a data atual",
"dynamicIconDateEmptyInfo": "Limpar a data, o ícone do calendário exibirá dinamicamente a data atual",
"backlinkContainChildren": "Os backlinks contêm blocos filhos",
"backlinkContainChildrenTip": "Quando ativado, os blocos filhos serão incluídos no cálculo do backlink",
"entryNum": "Número de entradas",
@ -340,6 +340,9 @@
"filterOperatorIsAfter": "É depois de",
"filterOperatorIsOnOrBefore": "É em ou antes de",
"filterOperatorIsOnOrAfter": "É em ou depois de",
"filterQuantifierAny": "Qualquer",
"filterQuantifierAll": "Todos",
"filterQuantifierNone": "Nenhum",
"asc": "Ascendente",
"desc": "Descendente",
"hideCol": "Ocultar campo",
@ -1121,7 +1124,7 @@
"fileTree3": "Nenhuma confirmação necessária ao excluir documentos",
"fileTree4": "Se não estiver ativado, uma caixa de confirmação aparecerá toda vez que você excluir um documento",
"fileTree5": "Local de salvamento do documento criado por Ref",
"fileTree6": "Ao usar <code class='fn__code'>((</code>, o caminho de salvamento do novo documento (por exemplo, <code class='fn__code'>/folder1/{{now | date \"20060102150405\"}}/</code>)",
"fileTree6": "Ao usar <code class='fn__code'>((</code> ou <code class='fn__code'>[[</code>, o caminho de salvamento do novo documento (por exemplo, <code class='fn__code'>/folder1/{{now | date \"20060102150405\"}}/</code>)",
"fileTree7": "Abrir na aba atual",
"fileTree8": "A nova aba de documento substituirá a aba não modificada",
"fileTree9": "Fechar todas as abas na inicialização",

View file

@ -54,7 +54,7 @@
"dataRepoAutoPurgeRetentionIndexesDaily": "Количество снимков данных в день",
"fields": "Атрибут",
"dynamicIcon": "Динамическая иконка",
"dynamicIconDateEmptyInfo": "Если дата пуста, иконка календаря будет динамически отображать сегодняшнюю дату",
"dynamicIconDateEmptyInfo": "Очистить дату, иконка календаря будет динамически отображать текущую дату",
"backlinkContainChildren": "Включать ли дочерние блоки в обратные ссылки",
"backlinkContainChildrenTip": "После включения дочерние блоки будут включены в расчет обратных ссылок",
"entryNum": "Количество записей",
@ -340,6 +340,9 @@
"filterOperatorIsAfter": "Находится после",
"filterOperatorIsOnOrBefore": "Находится на или до",
"filterOperatorIsOnOrAfter": "Находится на или после",
"filterQuantifierAny": "Любой",
"filterQuantifierAll": "Все",
"filterQuantifierNone": "Нет",
"asc": "По возрастанию",
"desc": "По убыванию",
"hideCol": "Скрыть колонку",
@ -1121,7 +1124,7 @@
"fileTree3": "Подтверждение не требуется при удалении документов",
"fileTree4": "Если не включено, будет появляться окно подтверждения каждый раз при удалении документа",
"fileTree5": "Сохранить местоположение созданного документа с ссылкой",
"fileTree6": "При использовании <code class='fn__code'>((</code> путь для нового документа (например, <code class='fn__code'>/folder1/{{now | date \"20060102150405\"}}/</code>)",
"fileTree6": "При использовании <code class='fn__code'>((</code> или <code class='fn__code'>[[</code> путь для нового документа (например, <code class='fn__code'>/folder1/{{now | date \"20060102150405\"}}/</code>)",
"fileTree7": "Открыть в текущей вкладке",
"fileTree8": "Вкладка нового открытого документа заменит неизмененную вкладку",
"fileTree9": "Закрыть все вкладки при запуске",

View file

@ -54,7 +54,7 @@
"dataRepoAutoPurgeRetentionIndexesDaily": "數據快照每天保留個數",
"fields": "欄位",
"dynamicIcon": "動態圖標",
"dynamicIconDateEmptyInfo": "若日期為空,日曆圖示將動態顯示當天日期",
"dynamicIconDateEmptyInfo": "清除日期,日曆圖示將動態顯示當天日期",
"backlinkContainChildren": "反向鏈接包含子塊",
"backlinkContainChildrenTip": "啟用後子塊將被納入到反向鏈接計算中",
"entryNum": "條目數",
@ -340,6 +340,9 @@
"filterOperatorIsAfter": "晚於",
"filterOperatorIsOnOrBefore": "早於或等於",
"filterOperatorIsOnOrAfter": "晚於或等於",
"filterQuantifierAny": "任一",
"filterQuantifierAll": "所有",
"filterQuantifierNone": "沒有",
"asc": "升序",
"desc": "降序",
"hideCol": "隱藏欄位",
@ -1121,7 +1124,7 @@
"fileTree3": "刪除文檔時不需要確認",
"fileTree4": "不啟用時每次刪除文檔都會彈出確認框",
"fileTree5": "塊引新建文檔存放位置",
"fileTree6": "使用 <code class='fn__code'>((</code> 時新建文檔的存放路徑(例如 <code class='fn__code'>/folder1/{{now | date \"20060102150405\"}}/</code>",
"fileTree6": "使用 <code class='fn__code'>((</code> 或 <code class='fn__code'>[[</code> 時新建文檔的存放路徑(例如 <code class='fn__code'>/folder1/{{now | date \"20060102150405\"}}/</code>",
"fileTree7": "在當前分頁中打開",
"fileTree8": "新打開的文檔分頁將會替換沒有修改過的分頁",
"fileTree9": "啟動時關閉所有分頁",

View file

@ -54,7 +54,7 @@
"dataRepoAutoPurgeRetentionIndexesDaily": "数据快照每天保留个数",
"fields": "字段",
"dynamicIcon": "动态图标",
"dynamicIconDateEmptyInfo": "若日期为空,日历图标将动态显示当天日期",
"dynamicIconDateEmptyInfo": "清除日期,日历图标将动态显示当天日期",
"backlinkContainChildren": "反向链接包含子块",
"backlinkContainChildrenTip": "启用后子块将被纳入到反向链接计算中",
"entryNum": "条目数",
@ -340,6 +340,9 @@
"filterOperatorIsAfter": "晚于",
"filterOperatorIsOnOrBefore": "早于或等于",
"filterOperatorIsOnOrAfter": "晚于或等于",
"filterQuantifierAny": "任一",
"filterQuantifierAll": "所有",
"filterQuantifierNone": "没有",
"asc": "升序",
"desc": "降序",
"hideCol": "隐藏字段",
@ -1121,7 +1124,7 @@
"fileTree3": "删除文档时不需要确认",
"fileTree4": "不启用时每次删除文档都会弹出确认框",
"fileTree5": "块引新建文档存放位置",
"fileTree6": "使用 <code class='fn__code'>((</code> 时新建文档的存放路径(例如 <code class='fn__code'>/folder1/{{now | date \"20060102150405\"}}/</code>",
"fileTree6": "使用 <code class='fn__code'>((</code> 或 <code class='fn__code'>[[</code> 时新建文档的存放路径(例如 <code class='fn__code'>/folder1/{{now | date \"20060102150405\"}}/</code>",
"fileTree7": "在当前页签中打开",
"fileTree8": "新打开的文档页签将会替换没有修改过的页签",
"fileTree9": "启动时关闭所有页签",

View file

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

View file

@ -21,7 +21,7 @@ Below are the detailed changes in this version.
* [Database cells support vertical dragging to fill values](https://github.com/siyuan-note/siyuan/issues/12907)
* [Improve document tree movement and its animation](https://github.com/siyuan-note/siyuan/issues/12914)
* [Improve S3/WebDAV data sync config](https://github.com/siyuan-note/siyuan/issues/12923)
* [The Publishing service no longer support export](https://github.com/siyuan-note/siyuan/issues/12928)
* [The Publish service no longer support export](https://github.com/siyuan-note/siyuan/issues/12928)
* [Display document title in data history preview area](https://github.com/siyuan-note/siyuan/issues/12948)
* [Improve parsing `<img>` when importing markdown](https://github.com/siyuan-note/siyuan/issues/12956)
* [Improve parsing of YAML Front Matter when importing Markdown](https://github.com/siyuan-note/siyuan/issues/12962)

View file

@ -0,0 +1,48 @@
## Overview
This version improves some details.
## Changelogs
Below are the detailed changes in this version.
### Enhancement
* [Copy/Cut folded heading changed to copy/cut `Headings and Bottom Blocks` and support multiple headings copy/cut](https://github.com/siyuan-note/siyuan/issues/8019)
* [Improve database field editing menu](https://github.com/siyuan-note/siyuan/issues/15185)
* [Database rollup field filtering rules support "Any", "All", and "None"](https://github.com/siyuan-note/siyuan/issues/15609)
* [Improve flashcards](https://github.com/siyuan-note/siyuan/issues/15699)
* [If the new data created in the database is in a collapsed group, the edit box will not pop up](https://github.com/siyuan-note/siyuan/issues/15728)
* [Select Copy in the code block to copy only the plain text](https://github.com/siyuan-note/siyuan/issues/15733)
* [Use the default sorting when the search content in the code block language prompt is empty](https://github.com/siyuan-note/siyuan/issues/15737)
* [Improve database rollup field filtering](https://github.com/siyuan-note/siyuan/issues/15740)
* [Improve database date field filtering](https://github.com/siyuan-note/siyuan/issues/15744)
* [Disable editing when adding options to the database](https://github.com/siyuan-note/siyuan/issues/15751)
* [After dragging database entries across groups, groups in other views need to be updated](https://github.com/siyuan-note/siyuan/issues/15755)
* [Browser clipping extension supports clipping page to the database](https://github.com/siyuan-note/siyuan/issues/15758)
* [Hide the bottom gesture navigation bar on Android](https://github.com/siyuan-note/siyuan/issues/15763)
* [Improve database group view performance](https://github.com/siyuan-note/siyuan/issues/15764)
* [Dragging multiple files into the editor will cause them to be opened by the default program](https://github.com/siyuan-note/siyuan/pull/15773)
* [Improve HTML table clipping](https://github.com/siyuan-note/siyuan/issues/15781)
* [Automatically create a new document when clicking on a notebook without documents](https://github.com/siyuan-note/siyuan/issues/15782)
* [Add field `disabledInPublish` to the code snippet to indicate whether it is disabled in the publish service](https://github.com/siyuan-note/siyuan/issues/15806)
### Bugfix
* [PDF files with too long file names cannot generate annotated images](https://github.com/siyuan-note/siyuan/issues/15739)
* [The video block network address is incorrect](https://github.com/siyuan-note/siyuan/issues/15741)
* [The database date field cannot paste the time of 0 o'clock](https://github.com/siyuan-note/siyuan/issues/15742)
* [Read-only mode cannot be set in preview mode](https://github.com/siyuan-note/siyuan/issues/15756)
* [Exception when inserting batch files into the editor](https://github.com/siyuan-note/siyuan/issues/15768)
* [When using certain input methods, punctuation characters are inserted twice after pasting a link at the end of a block](https://github.com/siyuan-note/siyuan/issues/15801)
### Development
* [Add field `disabledInPublish` to the marketplace plugin package metadata to indicate whether it is disabled in the publish service](https://github.com/siyuan-note/siyuan/issues/11730)
* [Add plugin function `expandDocTree`](https://github.com/siyuan-note/siyuan/issues/15639)
* [Improve kernel API `appendBlock`, `insertBlock` and `prependBlock`](https://github.com/siyuan-note/siyuan/issues/15798)
## Download
* [B3log](https://b3log.org/siyuan/en/download.html)
* [GitHub](https://github.com/siyuan-note/siyuan/releases)

View file

@ -0,0 +1,48 @@
## 概述
該版本改進了一些細節。
## 變更記錄
以下是此版本中的詳細變更。
### 改進功能
* [折疊標題的複製/剪切改為複製/剪切 `標題及其下方塊`,並支援多標題複製/剪切](https://github.com/siyuan-note/siyuan/issues/8019)
* [改進資料庫欄位編輯選單](https://github.com/siyuan-note/siyuan/issues/15185)
* [資料庫匯總欄位篩選規則支援「任一」、「所有」和「沒有」](https://github.com/siyuan-note/siyuan/issues/15609)
* [改良閃卡](https://github.com/siyuan-note/siyuan/issues/15699)
* [資料庫中新建資料如果在折疊分組內,不再彈出編輯框](https://github.com/siyuan-note/siyuan/issues/15728)
* [程式碼區塊選擇複製時僅複製純文字](https://github.com/siyuan-note/siyuan/issues/15733)
* [程式碼區塊語言提示搜尋內容為空時使用預設排序](https://github.com/siyuan-note/siyuan/issues/15737)
* [改進資料庫匯總欄位篩選](https://github.com/siyuan-note/siyuan/issues/15740)
* [改進資料庫日期欄位篩選](https://github.com/siyuan-note/siyuan/issues/15744)
* [資料庫新增選項時禁止編輯](https://github.com/siyuan-note/siyuan/issues/15751)
* [資料庫條目跨分組拖曳後,同步更新其他檢視中的分組](https://github.com/siyuan-note/siyuan/issues/15755)
* [瀏覽器剪藏擴充功能支援剪藏頁面到資料庫](https://github.com/siyuan-note/siyuan/issues/15758)
* [Android 隱藏底部手勢導覽列](https://github.com/siyuan-note/siyuan/issues/15763)
* [提升資料庫分組檢視效能](https://github.com/siyuan-note/siyuan/issues/15764)
* [拖入多個檔案到編輯器不再被預設程式開啟](https://github.com/siyuan-note/siyuan/pull/15773)
* [改進 HTML 表格剪藏](https://github.com/siyuan-note/siyuan/issues/15781)
* [點選無文件的筆記本時自動新建文件](https://github.com/siyuan-note/siyuan/issues/15782)
* [程式碼片段新增欄位 `disabledInPublish`,用於識別發佈服務中是否已停用](https://github.com/siyuan-note/siyuan/issues/15806)
### 修復缺陷
* [PDF 檔名過長無法產生標註圖片](https://github.com/siyuan-note/siyuan/issues/15739)
* [影片區塊網路網址不正確](https://github.com/siyuan-note/siyuan/issues/15741)
* [資料庫日期欄位無法貼上 0 點時間](https://github.com/siyuan-note/siyuan/issues/15742)
* [預覽模式下無法設定唯讀模式](https://github.com/siyuan-note/siyuan/issues/15756)
* [批次插入檔案到編輯器時異常](https://github.com/siyuan-note/siyuan/issues/15768)
* [部分輸入法在區塊末尾貼上連結後標點符號重複插入](https://github.com/siyuan-note/siyuan/issues/15801)
### 開發者
* [集市插件包元資料新增欄位 `disabledInPublish`,用於識別發布服務中是否已停用](https://github.com/siyuan-note/siyuan/issues/11730)
* [新增外掛程式 `expandDocTree`](https://github.com/siyuan-note/siyuan/issues/15639)
* [改進內核 API `appendBlock`、`insertBlock` 和 `prependBlock`](https://github.com/siyuan-note/siyuan/issues/15798)
## 下載
* [B3log](https://b3log.org/siyuan/download.html)
* [GitHub](https://github.com/siyuan-note/siyuan/releases)

View file

@ -0,0 +1,48 @@
## 概述
该版本改进了一些细节。
## 变更记录
以下是此版本中的详细变更。
### 改进功能
* [折叠标题的复制/剪切改为复制/剪切 `标题及其下方块`,并支持多标题复制/剪切](https://github.com/siyuan-note/siyuan/issues/8019)
* [改进数据库字段编辑菜单](https://github.com/siyuan-note/siyuan/issues/15185)
* [数据库汇总字段筛选规则支持“任一”、“所有”和“没有”](https://github.com/siyuan-note/siyuan/issues/15609)
* [改进闪卡](https://github.com/siyuan-note/siyuan/issues/15699)
* [数据库中新建数据如果在折叠分组内,不再弹出编辑框](https://github.com/siyuan-note/siyuan/issues/15728)
* [代码块选择复制时仅复制纯文本](https://github.com/siyuan-note/siyuan/issues/15733)
* [代码块语言提示搜索内容为空时使用默认排序](https://github.com/siyuan-note/siyuan/issues/15737)
* [改进数据库汇总字段筛选](https://github.com/siyuan-note/siyuan/issues/15740)
* [改进数据库日期字段筛选](https://github.com/siyuan-note/siyuan/issues/15744)
* [数据库添加选项时禁止编辑](https://github.com/siyuan-note/siyuan/issues/15751)
* [数据库条目跨分组拖动后,同步更新其他视图中的分组](https://github.com/siyuan-note/siyuan/issues/15755)
* [浏览器剪藏扩展支持剪藏页面到数据库](https://github.com/siyuan-note/siyuan/issues/15758)
* [Android 隐藏底部手势导航栏](https://github.com/siyuan-note/siyuan/issues/15763)
* [提升数据库分组视图性能](https://github.com/siyuan-note/siyuan/issues/15764)
* [拖入多个文件到编辑器不再被默认程序打开](https://github.com/siyuan-note/siyuan/pull/15773)
* [改进 HTML 表格剪藏](https://github.com/siyuan-note/siyuan/issues/15781)
* [点击无文档的笔记本时自动新建文档](https://github.com/siyuan-note/siyuan/issues/15782)
* [代码片段新增字段 `disabledInPublish`,用于标识发布服务中是否禁用](https://github.com/siyuan-note/siyuan/issues/15806)
### 修复缺陷
* [PDF 文件名过长无法生成标注图片](https://github.com/siyuan-note/siyuan/issues/15739)
* [视频块网络地址不正确](https://github.com/siyuan-note/siyuan/issues/15741)
* [数据库日期字段无法粘贴 0 点时间](https://github.com/siyuan-note/siyuan/issues/15742)
* [预览模式下无法设置只读模式](https://github.com/siyuan-note/siyuan/issues/15756)
* [批量插入文件到编辑器时异常](https://github.com/siyuan-note/siyuan/issues/15768)
* [部分输入法在块末尾粘贴链接后标点符号重复插入](https://github.com/siyuan-note/siyuan/issues/15801)
### 开发者
* [集市插件包元数据新增字段 `disabledInPublish`,用于标识发布服务中是否禁用](https://github.com/siyuan-note/siyuan/issues/11730)
* [新增插件函数 `expandDocTree`](https://github.com/siyuan-note/siyuan/issues/15639)
* [改进内核 API `appendBlock`、`insertBlock` 和 `prependBlock`](https://github.com/siyuan-note/siyuan/issues/15798)
## 下载
* [B3log](https://b3log.org/siyuan/download.html)
* [GitHub](https://github.com/siyuan-note/siyuan/releases)

View file

@ -3820,7 +3820,7 @@
"Children": [
{
"Type": "NodeText",
"Data": "In the code block, only select the content of the code block"
"Data": "Double press to select all loaded content blocks in the document; in the code block, only select the content of the code block"
}
]
}

View file

@ -102,7 +102,7 @@
},
{
"Type": "NodeText",
"Data": " to enter the publishing service settings panel."
"Data": " to enter the publish service settings panel."
}
]
}
@ -252,7 +252,7 @@
"Children": [
{
"Type": "NodeText",
"Data": "If access control is required for the publishing service:"
"Data": "If access control is required for the publish service:"
}
]
},
@ -333,7 +333,7 @@
"Children": [
{
"Type": "NodeText",
"Data": "When enabled, the publishing service will use the "
"Data": "When enabled, the publish service will use the "
},
{
"Type": "NodeTextMark",
@ -482,7 +482,7 @@
"Children": [
{
"Type": "NodeText",
"Data": "After enabling the publishing service, visitors can browse the content of the entire workspace."
"Data": "After enabling the publish service, visitors can browse the content of the entire workspace."
}
]
}

View file

@ -3818,7 +3818,7 @@
"Children": [
{
"Type": "NodeText",
"Data": "代码块中使用时仅选中代码块中的内容"
"Data": "连续按下两次以选中文档中所有已加载的内容块;代码块中使用时仅选中代码块中的内容"
}
]
}

View file

@ -3808,7 +3808,7 @@
"Children": [
{
"Type": "NodeText",
"Data": "代碼塊中使用時僅選中代碼塊中的內容"
"Data": "連續按下兩次以選中文檔中所有已加載的內容塊;代碼塊中使用時僅選中代碼塊中的內容"
}
]
}

View file

@ -3775,7 +3775,7 @@
"Children": [
{
"Type": "NodeText",
"Data": "コードブロックではコードのみが選択されます"
"Data": "文書内のすべてのロード済みコンテンツブロックを選択するにはダブルプレスしてください;コードブロックではコードのみが選択されます"
}
]
}

View file

@ -1,10 +1,10 @@
{
"name": "SiYuan",
"version": "3.3.1",
"version": "3.3.2",
"description": "Refactor your thinking",
"homepage": "https://b3log.org/siyuan",
"main": "./electron/main.js",
"packageManager": "pnpm@10.15.0",
"packageManager": "pnpm@10.15.1",
"scripts": {
"lint": "eslint . --fix --cache",
"dev": "webpack --mode development",

6284
app/pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

View file

@ -6,7 +6,6 @@ import {processSync} from "../dialog/processSystem";
import {getCloudURL} from "./util/about";
import {openByMobile} from "../protyle/util/compatibility";
import {confirmDialog} from "../dialog/confirmDialog";
import {isKernelInMobile} from "../util/functions";
const renderProvider = (provider: number) => {
if (provider === 0) {

View file

@ -62,33 +62,31 @@ export const openSnippets = () => {
<div class="fn__flex-1" style="overflow:auto;padding: 16px 24px">
<div>
<div class="fn__flex">
<div class="fn__flex-1"></div>
<div class="b3-form__icon">
<div class="b3-form__icon fn__flex-1">
<svg class="b3-form__icon-icon"><use xlink:href="#iconSearch"></use></svg>
<input data-type="css" data-action="search" type="text" placeholder="${window.siyuan.languages.search}" class="b3-text-field b3-form__icon-input">
<input data-type="css" data-action="search" type="text" placeholder="${window.siyuan.languages.search}" class="b3-text-field b3-form__icon-input fn__block">
</div>
<div class="fn__space"></div>
<span aria-label="${window.siyuan.languages.addAttr} CSS" id="addCodeSnippetCSS" class="b3-tooltips b3-tooltips__sw block__icon block__icon--show">
<svg><use xlink:href="#iconAdd"></use></svg>
</span>
<div class="fn__space"></div>
<input data-action="toggleCSS" class="b3-switch b3-switch--side fn__flex-center" type="checkbox"${window.siyuan.config.snippet.enabledCSS ? " checked" : ""}>
<input data-action="toggleCSS" class="b3-switch fn__flex-center" type="checkbox"${window.siyuan.config.snippet.enabledCSS ? " checked" : ""}>
</div>
${cssHTML}
</div>
<div class="fn__none">
<div class="fn__flex">
<div class="fn__flex-1"></div>
<div class="b3-form__icon">
<div class="b3-form__icon fn__flex-1">
<svg class="b3-form__icon-icon"><use xlink:href="#iconSearch"></use></svg>
<input data-type="js" data-action="search" type="text" placeholder="${window.siyuan.languages.search}" class="b3-text-field b3-form__icon-input">
<input data-type="js" data-action="search" type="text" placeholder="${window.siyuan.languages.search}" class="b3-text-field b3-form__icon-input fn__block">
</div>
<div class="fn__space"></div>
<span aria-label="${window.siyuan.languages.addAttr} JS" id="addCodeSnippetJS" class="b3-tooltips b3-tooltips__sw block__icon block__icon--show">
<svg><use xlink:href="#iconAdd"></use></svg>
</span>
<div class="fn__space"></div>
<input data-action="toggleJS" class="b3-switch b3-switch--side fn__flex-center" type="checkbox"${window.siyuan.config.snippet.enabledJS ? " checked" : ""}>
<input data-action="toggleJS" class="b3-switch fn__flex-center" type="checkbox"${window.siyuan.config.snippet.enabledJS ? " checked" : ""}>
</div>
${jsHTML}
</div>
@ -120,7 +118,8 @@ export const openSnippets = () => {
type: target.id === "addCodeSnippetCSS" ? "css" : "js",
name: "",
content: "",
enabled: false
enabled: false,
disabledInPublish: false,
}));
event.stopPropagation();
event.preventDefault();
@ -195,13 +194,19 @@ const genSnippet = (options: ISnippet) => {
<div class="fn__hr--b"></div>
<div class="fn__flex">
<input type="text" class="fn__size200 b3-text-field" placeholder="${window.siyuan.languages.title}">
<div class="fn__space"></div>
<label class="fn__flex${window.siyuan.config.publish.enable ? "" : " fn__none"}">
<input data-type="disabledInPublish" type="checkbox" class="b3-switch fn__flex-center" ${options.disabledInPublish ? "" : " checked"}>
<div class="fn__space"></div>
<span class="fn__flex-center">${window.siyuan.languages.publishService}</span>
</label>
<div class="fn__flex-1"></div>
<div class="fn__space"></div>
<span aria-label="${window.siyuan.languages.remove}" data-action="remove" class="b3-tooltips b3-tooltips__sw block__icon block__icon--show">
<svg><use xlink:href="#iconTrashcan"></use></svg>
</span>
<div class="fn__space"></div>
<input data-type="snippet" class="b3-switch b3-switch--side fn__flex-center" type="checkbox"${options.enabled ? " checked" : ""}>
<input data-type="snippet" class="b3-switch fn__flex-center" type="checkbox"${options.enabled ? " checked" : ""}>
</div>
<div class="fn__hr"></div>
<textarea class="fn__block b3-text-field" placeholder="${window.siyuan.languages.codeSnippet}" style="resize: vertical;font-family:var(--b3-font-family-code)" spellcheck="false"></textarea>
@ -229,11 +234,12 @@ const setSnippet = (dialog: Dialog, oldSnippets: ISnippet[], removeIds: string[]
const snippets: ISnippet[] = [];
dialog.element.querySelectorAll("[data-id]").forEach((item) => {
snippets.push({
disabledInPublish: !(item.querySelector('.b3-switch[data-type="disabledInPublish"]') as HTMLInputElement).checked,
id: item.getAttribute("data-id"),
name: item.querySelector("input").value,
type: item.getAttribute("data-type"),
content: item.querySelector("textarea").value,
enabled: (item.querySelector(".b3-switch") as HTMLInputElement).checked
enabled: (item.querySelector('.b3-switch[data-type="snippet"]') as HTMLInputElement).checked
});
});
if (objEquals(oldSnippets, snippets) &&

View file

@ -335,7 +335,7 @@ export const openEmojiPanel = (id: string, type: "doc" | "notebook" | "av", posi
<span class="fn__space--small"></span>
<input type="date" max="9999-12-31" class="b3-text-field fn__flex-1" value="${dynamicCurrentObj.date}"/>
<span class="fn__space--small"></span>
<span class="ariaLabel block__icon block__icon--show" aria-label="${window.siyuan.languages.dynamicIconDateEmptyInfo}"><svg><use xlink:href="#iconInfo"></use></svg></span>
<span data-action="clearDate" class="ariaLabel block__icon block__icon--show" aria-label="${window.siyuan.languages.dynamicIconDateEmptyInfo}"><svg><use xlink:href="#iconTrashcan"></use></svg></span>
<span class="fn__space"></span>
</div>
<div class="fn__hr"></div>
@ -630,6 +630,10 @@ export const openEmojiPanel = (id: string, type: "doc" | "notebook" | "av", posi
dynamicTextElements[0].value = target.getAttribute("style").replace("background-color:", "");
dynamicTextElements[0].dispatchEvent(new CustomEvent("input"));
break;
} else if ("clearDate" === target.dataset.action) {
dynamicDateElement.value = "";
dynamicDateElement.dispatchEvent(new CustomEvent("change"));
break;
}
target = target.parentElement;
}

View file

@ -1153,7 +1153,13 @@ data-type="navigation-root" data-path="/">
path: liElement.getAttribute("data-path"),
}, response => {
if (response.data.path === "/" && response.data.files.length === 0) {
showMessage(window.siyuan.languages.emptyContent);
newFile({
app: this.app,
notebookId,
currentPath: "/",
useSavePath: false,
listDocTree: true,
});
return;
}
this.onLsHTML(response.data);

View file

@ -4,7 +4,6 @@ import {Model} from "../../layout/Model";
import {Constants} from "../../constants";
import {getDisplayName, pathPosix, setNoteBook} from "../../util/pathName";
import {initFileMenu, initNavigationMenu, sortMenu} from "../../menus/navigation";
import {showMessage} from "../../dialog/message";
import {fetchPost, fetchSyncPost} from "../../util/fetch";
import {genUUID} from "../../util/genID";
import {openMobileFileById} from "../editor";
@ -649,7 +648,13 @@ export class MobileFiles extends Model {
path: liElement.getAttribute("data-path"),
}, response => {
if (response.data.path === "/" && response.data.files.length === 0) {
showMessage(window.siyuan.languages.emptyContent);
newFile({
app: this.app,
notebookId,
currentPath: "/",
useSavePath: false,
listDocTree: true,
});
return;
}
this.onLsHTML(response.data);

View file

@ -236,15 +236,28 @@ const getActiveEditor = (wndActive = true) => {
if (!editor && !wndActive) {
let activeTime = 0;
allEditor.forEach(item => {
const headerElement = item.protyle?.model.parent.headElement;
if (headerElement && headerElement.classList.contains("item--focus") && parseInt(headerElement.dataset.activetime) > activeTime) {
activeTime = parseInt(headerElement.dataset.activetime);
let headerElement = item.protyle.model?.parent.headElement;
if (!headerElement && item.protyle.element.getBoundingClientRect().height > 0) {
const tabBodyElement = item.protyle.element.closest(".fn__flex-1[data-id]");
if (tabBodyElement) {
headerElement = document.querySelector(`.layout-tab-bar .item[data-id="${tabBodyElement.getAttribute("data-id")}"]`);
}
}
if (headerElement) {
if (headerElement.classList.contains("item--focus") && parseInt(headerElement.dataset.activetime) > activeTime) {
activeTime = parseInt(headerElement.dataset.activetime);
editor = item;
}
} else if (item.protyle.element.getBoundingClientRect().height > 0) {
editor = item;
}
});
}
/// #else
editor = window.siyuan.mobile.popEditor || window.siyuan.mobile.editor;
if (editor?.protyle.element.classList.contains("fn__none")) {
return undefined;
}
/// #endif
return editor;
};
@ -267,9 +280,12 @@ export const expandDocTree = async (options: {
options.isSetCurrent = true;
}
if (isNotebook) {
liElement = file.element.querySelector(`.b3-list[data-url="${options.id}"]`).firstElementChild as HTMLElement;
liElement = file.element.querySelector(`.b3-list[data-url="${options.id}"]`)?.firstElementChild as HTMLElement;
} else {
const response = await fetchSyncPost("api/block/getBlockInfo", {id: options.id});
if (response.code === -1) {
return;
}
notebookId = response.data.box;
liElement = await file.selectItem(response.data.box, response.data.path, undefined, undefined, options.isSetCurrent);
}

View file

@ -18,7 +18,7 @@ export const openTopBarMenu = (app: App, target?: Element) => {
openSetting(app).element.querySelector('.b3-tab-bar [data-name="bazaar"]').dispatchEvent(new CustomEvent("click"));
}
});
menu.addSeparator({id: "separator_1"}, isHuawei() || window.siyuan.config.readonly);
menu.addSeparator({id: "separator_1", ignore: isHuawei() || window.siyuan.config.readonly});
/// #endif
let hasPlugin = false;
app.plugins.forEach((plugin) => {

View file

@ -1682,7 +1682,7 @@ export class Gutter {
icon: "iconCopy",
label: `${window.siyuan.languages.copy} ${window.siyuan.languages.headings1}`,
click() {
fetchPost("/api/block/getHeadingChildrenDOM", {id}, (response) => {
fetchPost("/api/block/getHeadingChildrenDOM", {id, removeFoldAttr: true}, (response) => {
if (isInAndroid()) {
window.JSAndroid.writeHTMLClipboard(protyle.lute.BlockDOM2StdMd(response.data).trimEnd(), response.data + Constants.ZWSP);
} else if (isInHarmony()) {
@ -1698,7 +1698,7 @@ export class Gutter {
icon: "iconCut",
label: `${window.siyuan.languages.cut} ${window.siyuan.languages.headings1}`,
click() {
fetchPost("/api/block/getHeadingChildrenDOM", {id}, (response) => {
fetchPost("/api/block/getHeadingChildrenDOM", {id, removeFoldAttr: true}, (response) => {
if (isInAndroid()) {
window.JSAndroid.writeHTMLClipboard(protyle.lute.BlockDOM2StdMd(response.data).trimEnd(), response.data + Constants.ZWSP);
} else if (isInHarmony()) {

View file

@ -6,7 +6,7 @@ import {
focusByWbr,
getEditorRange,
getSelectionOffset,
getSelectionPosition, setLastNodeRange
getSelectionPosition,
} from "../util/selection";
import {genHintItemHTML, hintEmbed, hintRef, hintSlash} from "./extend";
import {getSavePath, newFile} from "../../util/newFile";
@ -559,16 +559,12 @@ ${genHintItemHTML(item)}
}, response => {
// https://github.com/siyuan-note/siyuan/issues/10133
protyle.toolbar.range = range;
protyle.toolbar.setInlineMark(protyle, "block-ref", "range", {
const refElement = protyle.toolbar.setInlineMark(protyle, "block-ref", "range", {
type: "id",
color: `${response.data}${Constants.ZWSP}${refIsS ? "s" : "d"}${Constants.ZWSP}${(refIsS ? fileNames[0] : realFileName).substring(0, window.siyuan.config.editor.blockRefDynamicAnchorTextMaxLen)}`
});
if (protyle.toolbar.range.endContainer.nodeType === 1 &&
protyle.toolbar.range.endContainer.childNodes[protyle.toolbar.range.endOffset]) {
const refElement = hasPreviousSibling(protyle.toolbar.range.endContainer.childNodes[protyle.toolbar.range.endOffset]) as HTMLElement;
if (refElement && refElement.nodeType === 1 && refElement.getAttribute("data-type") === "block-ref") {
setLastNodeRange(refElement as HTMLElement, protyle.toolbar.range, false);
}
if (refElement[0]) {
protyle.toolbar.range.setEnd(refElement[0].lastChild, refElement[0].lastChild.textContent.length);
}
protyle.toolbar.range.collapse(false);
});
@ -600,16 +596,12 @@ ${genHintItemHTML(item)}
tempElement.innerText = dynamicTexts[1];
}
}
protyle.toolbar.setInlineMark(protyle, "block-ref", "range", {
const refElement = protyle.toolbar.setInlineMark(protyle, "block-ref", "range", {
type: "id",
color: `${tempElement.getAttribute("data-id")}${Constants.ZWSP}${tempElement.getAttribute("data-subtype")}${Constants.ZWSP}${tempElement.textContent}`
});
if (protyle.toolbar.range.endContainer.nodeType === 1 &&
protyle.toolbar.range.endContainer.childNodes[protyle.toolbar.range.endOffset]) {
const refElement = hasPreviousSibling(protyle.toolbar.range.endContainer.childNodes[protyle.toolbar.range.endOffset]) as HTMLElement;
if (refElement && refElement.nodeType === 1 && refElement.getAttribute("data-type") === "block-ref") {
setLastNodeRange(refElement as HTMLElement, protyle.toolbar.range, false);
}
if (refElement[0]) {
protyle.toolbar.range.setEnd(refElement[0].lastChild, refElement[0].lastChild.textContent.length);
}
protyle.toolbar.range.collapse(false);
return;

View file

@ -517,6 +517,9 @@ export const popTextCell = (protyle: IProtyle, cellElements: HTMLElement[], type
html = `<input type="number" spellcheck="false" value="${cellElements[0].firstElementChild.getAttribute("data-content")}" ${style} class="b3-text-field">`;
} else {
if (["select", "mSelect"].includes(type)) {
if (blockElement.getAttribute("data-rendering") === "true") {
return;
}
openMenuPanel({protyle, blockElement, type: "select", cellElements});
} else if (type === "mAsset") {
openMenuPanel({protyle, blockElement, type: "asset", cellElements});

View file

@ -192,11 +192,7 @@ export const getEditHTML = (options: {
<input type="checkbox" data-type="wrap" class="b3-switch b3-switch--menu"${colData.wrap ? " checked" : ""}>
</label>`;
if (colData.type !== "block") {
html += `<button class="b3-menu__item" data-type="${colData.hidden ? "showCol" : "hideCol"}">
<svg class="b3-menu__icon" style=""><use xlink:href="#icon${colData.hidden ? "Eye" : "Eyeoff"}"></use></svg>
<span class="b3-menu__label">${colData.hidden ? window.siyuan.languages.showCol : window.siyuan.languages.hideCol}</span>
</button>
<button class="b3-menu__item${colData.type === "relation" ? " fn__none" : ""}" data-type="duplicateCol">
html += `<button class="b3-menu__item${colData.type === "relation" ? " fn__none" : ""}" data-type="duplicateCol">
<svg class="b3-menu__icon" style=""><use xlink:href="#iconCopy"></use></svg>
<span class="b3-menu__label">${window.siyuan.languages.duplicate}</span>
</button>
@ -877,8 +873,88 @@ export const showColMenu = (protyle: IProtyle, blockElement: Element, cellElemen
});
}
});
menu.addSeparator({id: "separator_2"});
}
const isPin = cellElement.dataset.pin === "true";
menu.addItem({
id: isPin ? "unfreezeCol" : "freezeCol",
icon: isPin ? "iconUnpin" : "iconPin",
label: isPin ? window.siyuan.languages.unfreezeCol : window.siyuan.languages.freezeCol,
click() {
transaction(protyle, [{
action: "setAttrViewColPin",
id: colId,
avID,
data: !isPin,
blockID
}], [{
action: "setAttrViewColPin",
id: colId,
avID,
data: isPin,
blockID
}]);
updateAttrViewCellAnimation(blockElement.querySelector(`.av__row--header .av__cell[data-col-id="${colId}"]`), undefined, {pin: !isPin});
}
});
if (type !== "block") {
menu.addItem({
id: "hide",
icon: "iconEyeoff",
label: window.siyuan.languages.hide,
click() {
transaction(protyle, [{
action: "setAttrViewColHidden",
id: colId,
avID,
data: true,
blockID
}], [{
action: "setAttrViewColHidden",
id: colId,
avID,
data: false,
blockID
}]);
}
});
}
menu.addItem({
icon: "iconRefresh",
label: window.siyuan.languages.syncColWidth,
click() {
transaction(protyle, [{
action: "syncAttrViewTableColWidth",
keyID: colId,
avID,
id: blockElement.getAttribute(Constants.CUSTOM_SY_AV_VIEW),
}]);
}
});
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>
<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;
wrapElement.addEventListener("change", () => {
transaction(protyle, [{
action: "setAttrViewColWrap",
id: colId,
avID,
data: wrapElement.checked,
blockID
}], [{
action: "setAttrViewColWrap",
id: colId,
avID,
data: !wrapElement.checked,
blockID
}]);
menu.close();
});
}
});
menu.addSeparator({id: "separator_2"});
menu.addItem({
id: "insertColumnLeft",
icon: "iconInsertLeft",
@ -913,62 +989,6 @@ export const showColMenu = (protyle: IProtyle, blockElement: Element, cellElemen
});
}
});
if (type !== "block") {
menu.addItem({
id: "hide",
icon: "iconEyeoff",
label: window.siyuan.languages.hide,
click() {
transaction(protyle, [{
action: "setAttrViewColHidden",
id: colId,
avID,
data: true,
blockID
}], [{
action: "setAttrViewColHidden",
id: colId,
avID,
data: false,
blockID
}]);
}
});
}
const isPin = cellElement.dataset.pin === "true";
menu.addItem({
id: isPin ? "unfreezeCol" : "freezeCol",
icon: isPin ? "iconUnpin" : "iconPin",
label: isPin ? window.siyuan.languages.unfreezeCol : window.siyuan.languages.freezeCol,
click() {
transaction(protyle, [{
action: "setAttrViewColPin",
id: colId,
avID,
data: !isPin,
blockID
}], [{
action: "setAttrViewColPin",
id: colId,
avID,
data: isPin,
blockID
}]);
updateAttrViewCellAnimation(blockElement.querySelector(`.av__row--header .av__cell[data-col-id="${colId}"]`), undefined, {pin: !isPin});
}
});
menu.addItem({
icon: "iconRefresh",
label: window.siyuan.languages.syncColWidth,
click() {
transaction(protyle, [{
action: "syncAttrViewTableColWidth",
keyID: colId,
avID,
id: blockElement.getAttribute(Constants.CUSTOM_SY_AV_VIEW),
}]);
}
});
if (type !== "block") {
if (type !== "relation") {
menu.addItem({
@ -1006,9 +1026,11 @@ export const showColMenu = (protyle: IProtyle, blockElement: Element, cellElemen
title: window.siyuan.languages.removeColConfirm,
content: `<div class="b3-dialog__content">
${window.siyuan.languages.confirmRemoveRelationField
.replace("${x}", colData.key.name || window.siyuan.languages._kernel[272])
.replace("${y}", relResponse.data.av.name || window.siyuan.languages._kernel[267])
.replace("${z}", relResponse.data.av.keyValues.find((item: {key: {id: string}}) => item.key.id === colData.key.relation.backKeyID).key.name || window.siyuan.languages._kernel[272])}
.replace("${x}", colData.key.name || window.siyuan.languages._kernel[272])
.replace("${y}", relResponse.data.av.name || window.siyuan.languages._kernel[267])
.replace("${z}", relResponse.data.av.keyValues.find((item: {
key: { id: string }
}) => item.key.id === colData.key.relation.backKeyID).key.name || window.siyuan.languages._kernel[272])}
<div class="fn__hr--b"></div>
<button class="fn__block b3-button b3-button--remove" data-action="delete">${window.siyuan.languages.removeBothRelationField}</button>
<div class="fn__hr"></div>

View file

@ -18,9 +18,9 @@ export const getDateHTML = (cellElements: HTMLElement[]) => {
let value2 = "";
if (cellValue.isNotEmpty2) {
value2 = dayjs(cellValue.content2).format(isNotTime ? "YYYY-MM-DD" : "YYYY-MM-DD HH:mm");
const year = value.split("-")[0];
const year = value2.split("-")[0];
if (year.length !== 4) {
value = new Array(4 - year.length).fill(0).join("") + value;
value2 = new Array(4 - year.length).fill(0).join("") + value2;
}
} else if (cellValue.hasEndDate) {
value2 = dayjs(currentDate).format(isNotTime ? "YYYY-MM-DD" : "YYYY-MM-DD HH:mm");
@ -94,6 +94,8 @@ export const bindDateEvent = (options: {
}
});
inputElements[3].addEventListener("change", () => {
inputElements[0].value = "";
inputElements[1].value = "";
if (inputElements[3].checked) {
inputElements[0].setAttribute("type", "datetime-local");
inputElements[1].setAttribute("type", "datetime-local");

View file

@ -83,10 +83,10 @@ export const setFilter = async (options: {
rectTarget = options.protyle.wysiwyg.element.querySelector(`[data-col-id="${options.target.dataset.colId}"]`).getBoundingClientRect();
}
const blockID = options.blockElement.getAttribute("data-node-id");
let operationElement: HTMLSelectElement = undefined;
const menu = new Menu("set-filter-" + options.filter.column, () => {
const oldFilters = JSON.parse(JSON.stringify(options.data.view.filters));
const selectElement = menu.element.querySelector(".b3-select") as HTMLSelectElement;
if (!selectElement || !selectElement.value) {
if (!operationElement || !operationElement.value) {
return;
}
const newFilter: IAVFilter = {
@ -94,7 +94,7 @@ export const setFilter = async (options: {
value: {
type: options.filter.value.type
},
operator: selectElement.value as TAVFilterOperator
operator: operationElement.value as TAVFilterOperator
};
let hasMatch = false;
let newValue;
@ -132,8 +132,8 @@ export const setFilter = async (options: {
newValue = genCellValue(filterValue.type, {
isNotEmpty2: textElements[2].value !== "",
isNotEmpty: textElements[0].value !== "",
content: textElements[0].value ? new Date(textElements[0].value + " 00:00").getTime() : null,
content2: textElements[2].value ? new Date(textElements[2].value + " 00:00").getTime() : null,
content: textElements[0].value ? new Date(textElements[0].value + " 00:00").getTime() : 0,
content2: textElements[2].value ? new Date(textElements[2].value + " 00:00").getTime() : 0,
hasEndDate: newFilter.operator === "Is between",
isNotTime: true,
});
@ -156,6 +156,7 @@ export const setFilter = async (options: {
},
type: "rollup"
};
newFilter.quantifier = (menu.element.querySelector('.b3-select[data-type="quantifier"]') as HTMLSelectElement).value;
} else {
newFilter.value = newValue;
}
@ -211,6 +212,7 @@ export const setFilter = async (options: {
if (colData.type === "rollup") {
if (!colData.rollup || !colData.rollup.relationKeyID || !colData.rollup.keyID) {
showMessage(window.siyuan.languages.plsChoose);
document.querySelector(".av__panel")?.remove();
openMenuPanel({
protyle: options.protyle,
blockElement: options.blockElement,
@ -349,10 +351,21 @@ export const setFilter = async (options: {
<option ${"Is not empty" === options.filter.operator ? "selected" : ""} value="Is not empty">${window.siyuan.languages.filterOperatorIsNotEmpty}</option>`;
break;
}
if (options.filter.value.type === "rollup") {
menu.addItem({
iconHTML: "",
type: "readonly",
label: ` <select style="margin: 4px 0" class="b3-select fn__size200" data-type="quantifier">
<option ${(options.filter.quantifier === "" || options.filter.quantifier === "Any") ? "selected" : ""} value="Any">${window.siyuan.languages.filterQuantifierAny}</option>
<option ${"All" === options.filter.quantifier ? "selected" : ""} value="All">${window.siyuan.languages.filterQuantifierAll}</option>
<option ${"None" === options.filter.quantifier ? "selected" : ""} value="None">${window.siyuan.languages.filterQuantifierNone}</option>
</select>`
});
}
menu.addItem({
iconHTML: "",
type: "readonly",
label: `<select style="margin: 4px 0" class="b3-select fn__size200">${selectHTML}</select>`
label: `<select style="margin: 4px 0" class="b3-select fn__size200" data-type="operation">${selectHTML}</select>`
});
if (filterValue.type === "select" || filterValue.type === "mSelect") {
if (colData.options?.length > 0) {
@ -519,9 +532,9 @@ export const setFilter = async (options: {
}
}
});
const selectElement = (menu.element.querySelector(".b3-select") as HTMLSelectElement);
selectElement.addEventListener("change", () => {
toggleEmpty(selectElement, selectElement.value, filterValue.type);
operationElement = (menu.element.querySelector('.b3-select[data-type="operation"]') as HTMLSelectElement);
operationElement?.addEventListener("change", () => {
toggleEmpty(operationElement, operationElement.value, filterValue.type);
});
const dateTypeElement = menu.element.querySelector('.b3-select[data-type="dateType"]') as HTMLSelectElement;
dateTypeElement?.addEventListener("change", () => {
@ -570,7 +583,7 @@ export const setFilter = async (options: {
});
});
}
toggleEmpty(selectElement, selectElement.value, filterValue.type);
toggleEmpty(operationElement, operationElement.value, filterValue.type);
menu.open({x: rectTarget.left, y: rectTarget.bottom});
if (textElements.length > 0) {
textElements[0].select();
@ -638,18 +651,27 @@ export const getFiltersHTML = (data: IAV) => {
fields.find((item) => {
if (item.id === filter.column && item.type === filter.value.type) {
let filterText = "";
if (item.type === "rollup") {
if (filter.quantifier === "" || filter.quantifier === "Any") {
filterText = window.siyuan.languages.filterQuantifierAny + " ";
} else if (filter.quantifier === "All") {
filterText = window.siyuan.languages.filterQuantifierAll + " ";
} else if (filter.quantifier === "None") {
filterText = window.siyuan.languages.filterQuantifierNone + " ";
}
}
const filterValue = item.type === "rollup" ? (filter.value.rollup?.contents?.length > 0 ? filter.value.rollup.contents[0] : {type: "rollup"} as IAVCellValue) : filter.value;
if (filter.operator === "Is empty") {
filterText = ": " + window.siyuan.languages.filterOperatorIsEmpty;
filterText = ": " + filterText + window.siyuan.languages.filterOperatorIsEmpty;
} else if (filter.operator === "Is not empty") {
filterText = ": " + window.siyuan.languages.filterOperatorIsNotEmpty;
filterText = ": " + filterText + window.siyuan.languages.filterOperatorIsNotEmpty;
} else if (filter.operator === "Is false") {
if (filterValue.type !== "checkbox" || typeof filterValue.checkbox.checked === "boolean") {
filterText = ": " + window.siyuan.languages.unchecked;
filterText = ": " + filterText + window.siyuan.languages.unchecked;
}
} else if (filter.operator === "Is true") {
if (filterValue.type !== "checkbox" || typeof filterValue.checkbox.checked === "boolean") {
filterText = ": " + window.siyuan.languages.checked;
filterText = ": " + filterText + window.siyuan.languages.checked;
}
} else if (["created", "updated", "date"].includes(filterValue.type)) {
let dateValue = "";
@ -673,15 +695,15 @@ export const getFiltersHTML = (data: IAV) => {
}
if (dateValue) {
if (filter.operator === "Is between" && dateValue2) {
filterText = ` ${window.siyuan.languages.filterOperatorIsBetween} ${dateValue} ${dateValue2}`;
filterText = ` ${filterText}${window.siyuan.languages.filterOperatorIsBetween} ${dateValue} ${dateValue2}`;
} else if ("=" === filter.operator) {
filterText = `: ${dateValue}`;
filterText = `: ${filterText}${dateValue}`;
} else if ([">", "<"].includes(filter.operator)) {
filterText = ` ${filter.operator} ${dateValue}`;
filterText = ` ${filterText}${filter.operator} ${dateValue}`;
} else if (">=" === filter.operator) {
filterText = ` ${dateValue}`;
filterText = ` ${filterText}${dateValue}`;
} else if ("<=" === filter.operator) {
filterText = ` ${dateValue}`;
filterText = ` ${filterText}${dateValue}`;
}
}
} else if (["mSelect", "select"].includes(filterValue.type) && filterValue.mSelect?.length > 0) {
@ -693,41 +715,41 @@ export const getFiltersHTML = (data: IAV) => {
}
});
if ("Contains" === filter.operator) {
filterText = `: ${selectContent}`;
filterText = `: ${filterText}${selectContent}`;
} else if (filter.operator === "Does not contains") {
filterText = ` ${window.siyuan.languages.filterOperatorDoesNotContain} ${selectContent}`;
filterText = ` ${filterText}${window.siyuan.languages.filterOperatorDoesNotContain} ${selectContent}`;
} else if (filter.operator === "=") {
filterText = `: ${selectContent}`;
filterText = `: ${filterText}${selectContent}`;
} else if (filter.operator === "!=") {
filterText = ` ${window.siyuan.languages.filterOperatorIsNot} ${selectContent}`;
filterText = ` ${filterText}${window.siyuan.languages.filterOperatorIsNot} ${selectContent}`;
}
} else if (filterValue.type === "number" && filterValue.number && filterValue.number.isNotEmpty) {
if (["=", "!=", ">", "<"].includes(filter.operator)) {
filterText = ` ${filter.operator} ${filterValue.number.content}`;
filterText = ` ${filterText}${filter.operator} ${filterValue.number.content}`;
} else if (">=" === filter.operator) {
filterText = ` ${filterValue.number.content}`;
filterText = ` ${filterText}${filterValue.number.content}`;
} else if ("<=" === filter.operator) {
filterText = ` ${filterValue.number.content}`;
filterText = ` ${filterText}${filterValue.number.content}`;
}
} else if (["text", "block", "url", "phone", "email", "relation", "template"].includes(filterValue.type) && filterValue[filterValue.type as "text"]) {
const content = filterValue[filterValue.type as "text"].content || filterValue.relation?.blockIDs[0] || "";
if (content) {
if (["=", "Contains"].includes(filter.operator)) {
filterText = `: ${content}`;
filterText = `: ${filterText}${content}`;
} else if (filter.operator === "Does not contains") {
filterText = ` ${window.siyuan.languages.filterOperatorDoesNotContain} ${content}`;
filterText = ` ${filterText}${window.siyuan.languages.filterOperatorDoesNotContain} ${content}`;
} else if (filter.operator === "!=") {
filterText = ` ${window.siyuan.languages.filterOperatorIsNot} ${content}`;
filterText = ` ${filterText}${window.siyuan.languages.filterOperatorIsNot} ${content}`;
} else if ("Starts with" === filter.operator) {
filterText = ` ${window.siyuan.languages.filterOperatorStartsWith} ${content}`;
filterText = ` ${filterText}${window.siyuan.languages.filterOperatorStartsWith} ${content}`;
} else if ("Ends with" === filter.operator) {
filterText = ` ${window.siyuan.languages.filterOperatorEndsWith} ${content}`;
filterText = ` ${filterText}${window.siyuan.languages.filterOperatorEndsWith} ${content}`;
} else if ([">", "<"].includes(filter.operator)) {
filterText = ` ${filter.operator} ${content}`;
filterText = ` ${filterText}${filter.operator} ${content}`;
} else if (">=" === filter.operator) {
filterText = ` ${content}`;
filterText = ` ${filterText}${content}`;
} else if ("<=" === filter.operator) {
filterText = ` ${content}`;
filterText = ` ${filterText}${content}`;
}
}
}

View file

@ -264,7 +264,7 @@ const renderGroupTable = (options: ITableOptions) => {
options.data.view.groups.forEach((group: IAVTable) => {
if (group.groupHidden === 0) {
avBodyHTML += `${getGroupTitleHTML(group, group.rows.length)}
<div data-group-id="${group.id}" data-page-size="${group.pageSize}" data-dtype="${group.groupKey.type}" data-content="${group.groupValue.text?.content}" style="float: left" class="av__body${group.groupFolded ? " fn__none" : ""}">${getTableHTMLs(group, options.blockElement)}</div>`;
<div data-group-id="${group.id}" data-page-size="${group.pageSize}" data-dtype="${group.groupKey.type}" data-content="${Lute.EscapeHTMLStr(group.groupValue.text?.content)}" style="float: left" class="av__body${group.groupFolded ? " fn__none" : ""}">${getTableHTMLs(group, options.blockElement)}</div>`;
}
});
if (options.renderAll) {
@ -451,6 +451,7 @@ export const avRender = (element: Element, protyle: IProtyle, cb?: (data: IAV) =
}
if (avElements.length > 0) {
avElements.forEach((e: HTMLElement) => {
e.removeAttribute("data-rendering");
if (e.getAttribute("data-render") === "true" || hasClosestByClassName(e, "av__gallery-content")) {
return;
}

View file

@ -633,6 +633,7 @@ export const addColOptionOrCell = (protyle: IProtyle, data: IAV, cellElements: H
transaction(protyle, cellDoOperations, cellUndoOperations);
}
if (colData.type === "select") {
blockElement.setAttribute("data-rendering", "true");
menuElement.parentElement.dispatchEvent(new CustomEvent("click", {detail: "close"}));
} else {
const oldScroll = menuElement.querySelector(".b3-menu__items").scrollTop;

View file

@ -23,24 +23,30 @@ export const encodeBase64 = (text: string): string => {
}
};
const getSiyuanHTML = (text: IClipboardData) => {
const siyuanMatch = text.textHTML.match(/<!--data-siyuan='([^']+)'-->/);
export const getTextSiyuanFromTextHTML = (html: string) => {
const siyuanMatch = html.match(/<!--data-siyuan='([^']+)'-->/);
let textSiyuan = "";
let textHtml = html;
if (siyuanMatch) {
try {
if (typeof Buffer !== "undefined") {
const decodedBytes = Buffer.from(siyuanMatch[1], "base64");
text.siyuanHTML = decodedBytes.toString("utf8");
textSiyuan = decodedBytes.toString("utf8");
} else {
const decoder = new TextDecoder();
const bytes = Uint8Array.from(atob(siyuanMatch[1]), char => char.charCodeAt(0));
text.siyuanHTML = decoder.decode(bytes);
textSiyuan = decoder.decode(bytes);
}
// 移除注释节点,保持原有的 text/html 内容
text.textHTML = text.textHTML.replace(/<!--data-siyuan='[^']+'-->/, "");
textHtml = html.replace(/<!--data-siyuan='[^']+'-->/, "");
} catch (e) {
console.log("Failed to decode siyuan data from HTML comment:", e);
}
}
return {
textSiyuan,
textHtml
};
};
export const openByMobile = (uri: string) => {
@ -124,7 +130,9 @@ export const readClipboard = async () => {
if (item.types.includes("text/html")) {
const blob = await item.getType("text/html");
text.textHTML = await blob.text();
getSiyuanHTML(text);
const textObj = getTextSiyuanFromTextHTML(text.textHTML);
text.textHTML = textObj.textHtml;
text.siyuanHTML = textObj.textSiyuan;
}
if (item.types.includes("text/plain")) {
const blob = await item.getType("text/plain");
@ -145,11 +153,15 @@ export const readClipboard = async () => {
if (isInAndroid()) {
text.textPlain = window.JSAndroid.readClipboard();
text.textHTML = window.JSAndroid.readHTMLClipboard();
getSiyuanHTML(text);
const textObj = getTextSiyuanFromTextHTML(text.textHTML);
text.textHTML = textObj.textHtml;
text.siyuanHTML = textObj.textSiyuan;
} else if (isInHarmony()) {
text.textPlain = window.JSHarmony.readClipboard();
text.textHTML = window.JSHarmony.readHTMLClipboard();
getSiyuanHTML(text);
const textObj = getTextSiyuanFromTextHTML(text.textHTML);
text.textHTML = textObj.textHtml;
text.siyuanHTML = textObj.textSiyuan;
}
return text;
}

View file

@ -1418,6 +1418,7 @@ export const dropEvent = (protyle: IProtyle, editorElement: HTMLElement) => {
targetElement.classList.remove("dragover__bottom", "dragover__top", "dragover__left", "dragover__right");
}
} else if (!window.siyuan.dragElement && (event.dataTransfer.types[0] === "Files" || event.dataTransfer.types.includes("text/html"))) {
event.preventDefault();
// 外部文件拖入编辑器中或者编辑器内选中文字拖拽
// https://github.com/siyuan-note/siyuan/issues/9544
const avElement = hasClosestByClassName(event.target, "av");

View file

@ -446,12 +446,33 @@ export const insertHTML = (html: string, protyle: IProtyle, isBlock = false,
insertBefore = true;
}
}
(insertBefore ? Array.from(tempElement.content.children) : Array.from(tempElement.content.children).reverse()).forEach((item) => {
// https://github.com/siyuan-note/siyuan/issues/13232
if (item.getAttribute("data-type") === "NodeHeading" && item.getAttribute("fold") === "1") {
item.removeAttribute("fold");
}
// https://github.com/siyuan-note/siyuan/issues/15768
if (tempElement.content.firstChild.nodeType === 3 || (tempElement.content.firstChild.nodeType === 1 && tempElement.content.firstElementChild.tagName !== "DIV")) {
tempElement.innerHTML = protyle.lute.SpinBlockDOM(tempElement.innerHTML);
}
// let foldHeadingId = "";
// let foldHTML = "";
// 粘贴内容中包含折叠的子节点需后端插入到原节点中
// Array.from(tempElement.content.children).forEach((item) => {
// if (!item.getAttribute("parent-heading") && foldHeadingId && foldHTML) {
// fetchPost("/api/block/appendHeadingChildren", {id: foldHeadingId, dom: foldHTML});
// foldHeadingId = "";
// foldHTML = "";
// }
// if (item.getAttribute("data-type") === "NodeHeading" && item.getAttribute("fold") === "1") {
// foldHeadingId = item.getAttribute("data-node-id");
// return true;
// }
// if (foldHeadingId && item.getAttribute("parent-heading")) {
// foldHTML += item.outerHTML;
// }
// });
// if (foldHeadingId && foldHTML) {
// fetchPost("/api/block/appendHeadingChildren", {id: foldHeadingId, dom: foldHTML});
// }
(insertBefore ? Array.from(tempElement.content.children) : Array.from(tempElement.content.children).reverse()).find((item) => {
let addId = item.getAttribute("data-node-id");
const hasParentHeading = item.getAttribute("parent-heading");
if (addId === id) {
doOperation.push({
action: "update",
@ -476,10 +497,12 @@ export const insertHTML = (html: string, protyle: IProtyle, isBlock = false,
liElement.append(item);
item = liElement;
}
item.removeAttribute("parent-heading");
doOperation.push({
action: "insert",
data: item.outerHTML,
id: addId,
context: {ignoreProcess: hasParentHeading ? "true" : "false"},
nextID: insertBefore ? id : undefined,
previousID: insertBefore ? undefined : id
});
@ -488,10 +511,12 @@ export const insertHTML = (html: string, protyle: IProtyle, isBlock = false,
id: addId,
});
}
if (insertBefore) {
blockElement.before(item);
} else {
blockElement.after(item);
if (!hasParentHeading) {
if (insertBefore) {
blockElement.before(item);
} else {
blockElement.after(item);
}
}
if (!lastElement) {
lastElement = item;

View file

@ -1,7 +1,7 @@
import {Constants} from "../../constants";
import {uploadFiles, uploadLocalFiles} from "../upload";
import {processPasteCode, processRender} from "./processCode";
import {getLocalFiles, readText} from "./compatibility";
import {getLocalFiles, getTextSiyuanFromTextHTML, readText} from "./compatibility";
import {hasClosestBlock, hasClosestByAttribute, hasClosestByClassName} from "./hasClosest";
import {getEditorRange} from "./selection";
import {blockRender} from "../render/blockRender";
@ -288,6 +288,12 @@ export const paste = async (protyle: IProtyle, event: (ClipboardEvent | DragEven
if (textPlain.endsWith(Constants.ZWSP) && !textHTML && !siyuanHTML) {
siyuanHTML = textPlain.substr(0, textPlain.length - 1);
}
// 复制/剪切折叠标题需获取 siyuanHTML
if (textHTML && textPlain && !siyuanHTML) {
const textObj = getTextSiyuanFromTextHTML(textHTML);
siyuanHTML = textObj.textSiyuan;
textHTML = textObj.textHtml;
}
// 剪切复制中首位包含空格或仅有空格 https://github.com/siyuan-note/siyuan/issues/5667
if (!siyuanHTML) {
// process word
@ -380,10 +386,13 @@ export const paste = async (protyle: IProtyle, event: (ClipboardEvent | DragEven
}
if (types.includes("block-ref")) {
protyle.toolbar.setInlineMark(protyle, "block-ref", "range", {
const refElement = protyle.toolbar.setInlineMark(protyle, "block-ref", "range", {
type: "id",
color: `${linkElement.dataset.id}${Constants.ZWSP}s${Constants.ZWSP}${range.toString()}`
});
if (refElement[0]) {
protyle.toolbar.range.selectNodeContents(refElement[0]);
}
return;
}
if (types.includes("a")) {
@ -502,11 +511,15 @@ export const paste = async (protyle: IProtyle, event: (ClipboardEvent | DragEven
}
if (linkElement) {
const selectText = range.toString();
protyle.toolbar.setInlineMark(protyle, "a", "range", {
const aElements = protyle.toolbar.setInlineMark(protyle, "a", "range", {
type: "a",
color: `${linkElement.getAttribute("href")}${Constants.ZWSP}${selectText || linkElement.textContent}`
});
if (!selectText) {
if (aElements[0].lastChild) {
// https://github.com/siyuan-note/siyuan/issues/15801
range.setEnd(aElements[0].lastChild, aElements[0].lastChild.textContent.length);
}
range.collapse(false);
}
return;
@ -536,11 +549,14 @@ export const paste = async (protyle: IProtyle, event: (ClipboardEvent | DragEven
if (range.toString() !== "") {
const firstLine = textPlain.split("\n")[0];
if (isDynamicRef(textPlain)) {
protyle.toolbar.setInlineMark(protyle, "block-ref", "range", {
const refElement = protyle.toolbar.setInlineMark(protyle, "block-ref", "range", {
type: "id",
// range 不能 escape否则 https://github.com/siyuan-note/siyuan/issues/8359
color: `${textPlain.substring(2, 22 + 2)}${Constants.ZWSP}s${Constants.ZWSP}${range.toString()}`
});
if (refElement[0]) {
protyle.toolbar.range.selectNodeContents(refElement[0]);
}
return;
} else if (isFileAnnotation(firstLine)) {
protyle.toolbar.setInlineMark(protyle, "file-annotation-ref", "range", {

View file

@ -68,7 +68,7 @@ import {popSearch} from "../../mobile/menu/search";
import {BlockPanel} from "../../block/Panel";
import {copyPlainText, isInIOS, isMac, isOnlyMeta, readClipboard, encodeBase64} from "../util/compatibility";
import {MenuItem} from "../../menus/Menu";
import {fetchPost} from "../../util/fetch";
import {fetchPost, fetchSyncPost} from "../../util/fetch";
import {onGet} from "../util/onGet";
import {clearTableCell, isIncludeCell, setTableAlign} from "../util/table";
import {countBlockWord, countSelectWord} from "../../layout/status";
@ -256,7 +256,7 @@ export class WYSIWYG {
}
private bindCommonEvent(protyle: IProtyle) {
this.element.addEventListener("copy", (event: ClipboardEvent & { target: HTMLElement }) => {
this.element.addEventListener("copy", async (event: ClipboardEvent & { target: HTMLElement }) => {
window.siyuan.ctrlIsPressed = false; // https://github.com/siyuan-note/siyuan/issues/6373
// https://github.com/siyuan-note/siyuan/issues/4600
if (event.target.tagName === "PROTYLE-HTML" || event.target.localName === "input") {
@ -282,6 +282,8 @@ export class WYSIWYG {
}
let html = "";
let textPlain = "";
let isInCodeBlock = false;
let needClipboardWrite = false;
if (selectElements.length > 0) {
const isRefText = selectElements[0].getAttribute("data-reftext") === "true";
if (selectElements[0].getAttribute("data-type") === "NodeListItem" &&
@ -293,14 +295,24 @@ export class WYSIWYG {
html = selectElements[0].parentElement.outerHTML;
}
} else {
selectElements.forEach((item: HTMLElement, index) => {
for (let i = 0; i < selectElements.length; i++) {
const item = selectElements[i] as HTMLElement;
// 复制列表项中的块会变为复制列表项,因此不能使用 getTopAloneElement https://github.com/siyuan-note/siyuan/issues/8925
if (isRefText && index === 0) {
if (isRefText && i === 0) {
html += getTextStar(item) + "\n\n";
} else {
html += removeEmbed(item);
if (item.getAttribute("data-type") === "NodeHeading" && item.getAttribute("fold") === "1") {
needClipboardWrite = true;
const response = await fetchSyncPost("/api/block/getHeadingChildrenDOM", {
id: item.getAttribute("data-node-id"),
removeFoldAttr: false
});
html += response.data;
} else {
html += removeEmbed(item);
}
}
});
}
}
if (isRefText) {
selectElements[0].removeAttribute("data-reftext");
@ -380,6 +392,8 @@ export class WYSIWYG {
if (matchHeading) {
// 复制标题 https://github.com/siyuan-note/insider/issues/297
tempElement.append(headingElement.cloneNode(true));
// https://github.com/siyuan-note/siyuan/issues/13232
headingElement.removeAttribute("fold");
} else if (!["DIV", "TD", "TH", "TR"].includes(range.startContainer.parentElement.tagName)) {
// 复制行内元素 https://github.com/siyuan-note/insider/issues/191
tempElement.append(range.startContainer.parentElement.cloneNode(true));
@ -427,7 +441,7 @@ export class WYSIWYG {
if (isEndOfBlock(range)) {
textPlain = textPlain.replace(/\n$/, "");
}
html = textPlain;
isInCodeBlock = true;
} else if (hasClosestByTag(range.startContainer, "TD") || hasClosestByTag(range.startContainer, "TH")) {
tempElement.innerHTML = tempElement.innerHTML.replace(/<br>/g, "\n").replace(/<br\/>/g, "\n");
textPlain = tempElement.textContent.endsWith("\n") ? tempElement.textContent.replace(/\n$/, "") : tempElement.textContent;
@ -445,14 +459,25 @@ export class WYSIWYG {
.replace(new RegExp(Constants.ZWSP, "g"), "");
event.clipboardData.setData("text/plain", textPlain);
// 设置 text/siyuan 数据
enableLuteMarkdownSyntax(protyle);
const siyuanHTML = selectTableElement ? protyle.lute.HTML2BlockDOM(html) : html;
event.clipboardData.setData("text/siyuan", siyuanHTML);
restoreLuteMarkdownSyntax(protyle);
// 在 text/html 中插入注释节点,用于右键菜单粘贴时获取 text/siyuan 数据
event.clipboardData.setData("text/html", `<!--data-siyuan='${encodeBase64(siyuanHTML)}'-->` + (selectTableElement ? html : protyle.lute.BlockDOM2HTML(selectAVElement ? textPlain : html)));
if (!isInCodeBlock) {
enableLuteMarkdownSyntax(protyle);
const textSiyuan = selectTableElement ? protyle.lute.HTML2BlockDOM(html) : html;
event.clipboardData.setData("text/siyuan", textSiyuan);
restoreLuteMarkdownSyntax(protyle);
// 在 text/html 中插入注释节点,用于右键菜单粘贴时获取 text/siyuan 数据
const textHTML = `<!--data-siyuan='${encodeBase64(textSiyuan)}'-->` + (selectTableElement ? html : protyle.lute.BlockDOM2HTML(selectAVElement ? textPlain : html));
event.clipboardData.setData("text/html", textHTML);
if (needClipboardWrite) {
try {
await navigator.clipboard.write([new ClipboardItem({
["text/plain"]: textPlain,
["text/html"]: textHTML,
})]);
} catch (e) {
console.log("Copy write clipboard error:", e);
}
}
}
});
this.element.addEventListener("mousedown", (event: MouseEvent) => {
@ -1720,7 +1745,7 @@ export class WYSIWYG {
}
});
this.element.addEventListener("cut", (event: ClipboardEvent & { target: HTMLElement }) => {
this.element.addEventListener("cut", async (event: ClipboardEvent & { target: HTMLElement }) => {
window.siyuan.ctrlIsPressed = false; // https://github.com/siyuan-note/siyuan/issues/6373
if (protyle.disabled) {
return;
@ -1758,20 +1783,28 @@ export class WYSIWYG {
}
let html = "";
let textPlain = "";
let isInCodeBlock = false;
let needClipboardWrite = false;
if (selectElements.length > 0) {
if (selectElements[0].getAttribute("data-type") === "NodeListItem" &&
selectElements[0].parentElement.classList.contains("list") && // 反链复制列表项 https://github.com/siyuan-note/siyuan/issues/6555
selectElements[0].parentElement.childElementCount - 1 === selectElements.length) {
html = selectElements[0].parentElement.outerHTML;
} else {
selectElements.forEach(item => {
for (let i = 0; i < selectElements.length; i++) {
const item = selectElements[i];
const topElement = getTopAloneElement(item);
if (item.getAttribute("data-type") === "NodeHeading" && item.getAttribute("fold") === "1") {
html += removeEmbed(topElement).replace('fold="1"', "");
needClipboardWrite = true;
const response = await fetchSyncPost("/api/block/getHeadingChildrenDOM", {
id: item.getAttribute("data-node-id"),
removeFoldAttr: false
});
html += response.data;
} else {
html += removeEmbed(topElement);
}
});
}
if (selectElements[0].getAttribute("data-type") === "NodeListItem") {
html = `<div data-subtype="${selectElements[0].getAttribute("data-subtype")}" data-node-id="${Lute.NewNodeID()}" data-type="NodeList" class="list">${html}<div class="protyle-attr" contenteditable="false">${Constants.ZWSP}</div></div>`;
}
@ -1919,6 +1952,7 @@ export class WYSIWYG {
if (hasClosestByAttribute(range.startContainer, "data-type", "NodeCodeBlock") ||
hasClosestByTag(range.startContainer, "CODE")) {
textPlain = tempElement.textContent.replace(Constants.ZWSP, "");
isInCodeBlock = true;
}
// https://github.com/siyuan-note/siyuan/issues/4321
if (!nodeElement.classList.contains("table")) {
@ -1950,14 +1984,25 @@ export class WYSIWYG {
textPlain = textPlain.replace(/\u00A0/g, " "); // Replace non-breaking spaces with normal spaces when copying https://github.com/siyuan-note/siyuan/issues/9382
event.clipboardData.setData("text/plain", textPlain);
// 设置 text/siyuan 数据
enableLuteMarkdownSyntax(protyle);
const siyuanHTML = selectTableElement ? protyle.lute.HTML2BlockDOM(html) : html;
event.clipboardData.setData("text/siyuan", siyuanHTML);
restoreLuteMarkdownSyntax(protyle);
// 在 text/html 中插入注释节点,用于右键菜单粘贴时获取 text/siyuan 数据
event.clipboardData.setData("text/html", `<!--data-siyuan='${encodeBase64(siyuanHTML)}'-->` + (selectTableElement ? html : protyle.lute.BlockDOM2HTML(selectAVElement ? textPlain : html)));
if (!isInCodeBlock) {
enableLuteMarkdownSyntax(protyle);
const textSiyuan = selectTableElement ? protyle.lute.HTML2BlockDOM(html) : html;
restoreLuteMarkdownSyntax(protyle);
event.clipboardData.setData("text/siyuan", textSiyuan);
// 在 text/html 中插入注释节点,用于右键菜单粘贴时获取 text/siyuan 数据
const textHTML = `<!--data-siyuan='${encodeBase64(textSiyuan)}'-->` + (selectTableElement ? html : protyle.lute.BlockDOM2HTML(selectAVElement ? textPlain : html));
event.clipboardData.setData("text/html", textHTML);
if (needClipboardWrite) {
try {
await navigator.clipboard.write([new ClipboardItem({
["text/plain"]: textPlain,
["text/html"]: textHTML,
})]);
} catch (e) {
console.log("Cut write clipboard error:", e);
}
}
}
});
let beforeContextmenuRange: Range;

View file

@ -37,6 +37,10 @@ export const input = async (protyle: IProtyle, blockElement: HTMLElement, range:
} else if (type === "NodeBlockQueryEmbed") {
blockElement.lastElementChild.previousElementSibling.innerHTML = "<wbr>" + Constants.ZWSP;
} else if (type === "NodeMathBlock" || type === "NodeHTMLBlock") {
// https://github.com/siyuan-note/siyuan/issues/15761
if (blockElement.firstElementChild.firstChild.nodeType === 3) {
blockElement.firstElementChild.firstChild.remove();
}
blockElement.lastElementChild.previousElementSibling.lastElementChild.innerHTML = "<wbr>" + Constants.ZWSP;
} else if (type === "NodeIFrame" || type === "NodeWidget") {
blockElement.innerHTML = "<wbr>" + blockElement.firstElementChild.outerHTML + blockElement.lastElementChild.outerHTML;

View file

@ -12,7 +12,7 @@ import {
import {transaction, turnsIntoOneTransaction, turnsIntoTransaction, updateTransaction} from "./transaction";
import {cancelSB, genEmptyElement} from "../../block/util";
import {listOutdent, updateListOrder} from "./list";
import {setFold, zoomOut} from "../../menus/protyle";
import {zoomOut} from "../../menus/protyle";
import {preventScroll} from "../scroll/preventScroll";
import {hideElements} from "../ui/hideElements";
import {Constants} from "../../constants";
@ -23,6 +23,7 @@ import {hasClosestByClassName} from "../util/hasClosest";
import {getInstanceById} from "../../layout/util";
import {Tab} from "../../layout/Tab";
import {Backlink} from "../../layout/dock/Backlink";
import {fetchSyncPost} from "../../util/fetch";
export const removeBlock = async (protyle: IProtyle, blockElement: Element, range: Range, type: "Delete" | "Backspace" | "remove") => {
protyle.observerLoad?.disconnect();
@ -32,7 +33,7 @@ export const removeBlock = async (protyle: IProtyle, blockElement: Element, rang
if (selectElements?.length > 0) {
const deletes: IOperation[] = [];
const inserts: IOperation[] = [];
let sideElement;
let sideElement: Element | boolean;
let sideIsNext = false;
if (type === "Backspace") {
sideElement = selectElements[0].previousElementSibling;
@ -52,7 +53,8 @@ export const removeBlock = async (protyle: IProtyle, blockElement: Element, rang
let topParentElement: Element;
hideElements(["select"], protyle);
let foldPreviousId: string;
selectElements.find((item: HTMLElement) => {
for (let i = 0; i < selectElements.length; i++) {
const item = selectElements[i];
const topElement = getTopAloneElement(item);
topParentElement = topElement.parentElement;
const id = topElement.getAttribute("data-node-id");
@ -79,19 +81,23 @@ export const removeBlock = async (protyle: IProtyle, blockElement: Element, rang
sideIsNext = false;
}
if (topElement.getAttribute("data-type") === "NodeHeading" && topElement.getAttribute("fold") === "1") {
// https://github.com/siyuan-note/siyuan/issues/2188
setFold(protyle, topElement, undefined, true);
const foldTransaction = await fetchSyncPost("/api/block/getHeadingDeleteTransaction", {
id: topElement.getAttribute("data-node-id"),
});
deletes.push(...foldTransaction.data.doOperations.slice(1));
let previousID = topElement.previousElementSibling ? topElement.previousElementSibling.getAttribute("data-node-id") : "";
if (typeof foldPreviousId !== "undefined") {
previousID = foldPreviousId;
}
inserts.push({
action: "insert",
data: topElement.outerHTML,
id,
previousID: previousID,
parentID: topElement.parentElement.getAttribute("data-node-id") || protyle.block.parentID
foldTransaction.data.undoOperations.forEach((operationItem: IOperation, index: number) => {
operationItem.previousID = previousID;
if (index > 0) {
operationItem.context = {
ignoreProcess: "true"
};
}
});
inserts.push(...foldTransaction.data.undoOperations);
// 折叠块和非折叠块同时删除时撤销异常 https://github.com/siyuan-note/siyuan/issues/11312
let foldPreviousElement = getPreviousBlock(topElement);
while (foldPreviousElement && foldPreviousElement.childElementCount === 3) {
@ -104,15 +110,7 @@ export const removeBlock = async (protyle: IProtyle, blockElement: Element, rang
}
// https://github.com/siyuan-note/siyuan/issues/4422
topElement.firstElementChild.removeAttribute("contenteditable");
// 在折叠标题后输入文字,然后全选删除再撤销会重建索引。因此不能删除折叠标题后新输入的输入折叠标题下的内容
const nextElement = topElement.nextElementSibling;
if (nextElement) {
const nextType = nextElement.getAttribute("data-type");
if (nextType !== "NodeHeading" ||
(nextType === "NodeHeading" && nextElement.getAttribute("data-subtype") > topElement.getAttribute("data-subtype"))) {
return true;
}
}
topElement.remove();
} else {
let data = topElement.outerHTML; // 不能 spin ,否则 li 会变为 list
if (topElement.classList.contains("render-node") || topElement.querySelector("div.render-node")) {
@ -136,7 +134,7 @@ export const removeBlock = async (protyle: IProtyle, blockElement: Element, rang
}
topElement.remove();
}
});
}
if (sideElement) {
if (protyle.block.showAll && sideElement.classList.contains("protyle-wysiwyg") && protyle.wysiwyg.element.childElementCount === 0) {
setTimeout(() => {
@ -164,11 +162,11 @@ export const removeBlock = async (protyle: IProtyle, blockElement: Element, rang
// https://github.com/siyuan-note/siyuan/issues/10389
// https://github.com/siyuan-note/siyuan/issues/10899
if (type !== "Backspace" && sideIsNext) {
focusBlock(sideElement);
focusBlock(sideElement as Element);
} else {
focusBlock(sideElement, undefined, false);
focusBlock(sideElement as Element, undefined, false);
}
scrollCenter(protyle, sideElement);
scrollCenter(protyle, sideElement as Element);
if (listElement) {
inserts.push({
action: "update",

View file

@ -754,6 +754,9 @@ export const onTransaction = (protyle: IProtyle, operation: IOperation, isUndo:
return;
}
if (operation.action === "insert") {
if (operation.context?.ignoreProcess === "true") {
return;
}
const cursorElements = [];
if (operation.previousID) {
const previousElement = protyle.wysiwyg.element.querySelectorAll(`[data-node-id="${operation.previousID}"]`);

View file

@ -64,7 +64,7 @@ declare namespace Config {
*/
openHelp: boolean;
/**
* Publishing service
* Publish service
*
*/
publish: IPublish;
@ -1078,29 +1078,29 @@ declare namespace Config {
export type TLogLevel = "off" | "trace" | "debug" | "info" | "warn" | "error" | "fatal";
/**
* Publishing service
* Publish service
*/
export interface IPublish {
/**
* Whether to open the publishing service
* Whether to open the publish service
*/
enable: boolean;
/**
* The basic authentication settings of publishing service
* The basic authentication settings of publish service
*/
auth: IPublishAuth;
/**
* Port on which the publishing service listens
* Port on which the publish service listens
*/
port: number;
}
/**
* Publishing service authentication settings
* Publish service authentication settings
*/
export interface IPublishAuth {
/**
* Whether to enable basic authentication for publishing services
* Whether to enable basic authentication for publish services
*/
enable: boolean;
/**

View file

@ -369,6 +369,7 @@ interface ISnippet {
type: string;
enabled: boolean;
content: string;
disabledInPublish: boolean;
}
interface IInbox {
@ -897,6 +898,7 @@ interface IAVGallery extends IAVView {
interface IAVFilter {
column: string,
operator: TAVFilterOperator,
quantifier?: string,
value: IAVCellValue,
relativeDate?: relativeDate
relativeDate2?: relativeDate

View file

@ -104,6 +104,10 @@ export const objEquals = (a: any, b: any): boolean => {
};
export const duplicateNameAddOne = (name:string) => {
if (!name) {
return "";
}
const nameMatch = name.match(/^(.*) \((\d+)\)$/);
if (nameMatch) {
name = `${nameMatch[1]} (${parseInt(nameMatch[2]) + 1})`;

View file

@ -249,10 +249,13 @@ export const newFileBySelect = (protyle: IProtyle, selectText: string, nodeEleme
}, (idResponse) => {
const refText = newFileName.substring(0, window.siyuan.config.editor.blockRefDynamicAnchorTextMaxLen);
if (idResponse.data && idResponse.data.length > 0) {
protyle.toolbar.setInlineMark(protyle, "block-ref", "range", {
const refElement = protyle.toolbar.setInlineMark(protyle, "block-ref", "range", {
type: "id",
color: `${idResponse.data[0]}${Constants.ZWSP}d${Constants.ZWSP}${refText}`
});
if (refElement[0]) {
protyle.toolbar.range.selectNodeContents(refElement[0]);
}
} else {
fetchPost("/api/filetree/createDocWithMd", {
notebook: targetNotebookId,
@ -260,10 +263,13 @@ export const newFileBySelect = (protyle: IProtyle, selectText: string, nodeEleme
parentID: protyle.notebookId === targetNotebookId ? protyle.block.rootID : "",
markdown: ""
}, response => {
protyle.toolbar.setInlineMark(protyle, "block-ref", "range", {
const refElement = protyle.toolbar.setInlineMark(protyle, "block-ref", "range", {
type: "id",
color: `${response.data}${Constants.ZWSP}d${Constants.ZWSP}${refText}`
});
if (refElement[0]) {
protyle.toolbar.range.selectNodeContents(refElement[0]);
}
});
}
hideElements(["toolbar"], protyle);

View file

@ -1,5 +1,5 @@
const isNormalItem = (currentHintElement: HTMLElement, className: string) => {
return !currentHintElement.classList.contains(className) || currentHintElement.getBoundingClientRect().height === 0;
export const isAbnormalItem = (currentHintElement: HTMLElement, className: string) => {
return !currentHintElement || !currentHintElement.classList.contains(className) || currentHintElement.getBoundingClientRect().height === 0;
};
export const upDownHint = (listElement: Element, event: KeyboardEvent, classActiveName = "b3-list-item--focus", defaultElement?: Element) => {
@ -19,13 +19,13 @@ export const upDownHint = (listElement: Element, event: KeyboardEvent, classActi
currentHintElement.classList.remove(classActiveName);
currentHintElement = currentHintElement.nextElementSibling as HTMLElement;
while (currentHintElement && isNormalItem(currentHintElement, className)) {
while (isAbnormalItem(currentHintElement, className)) {
currentHintElement = currentHintElement.nextElementSibling as HTMLElement;
}
if (!currentHintElement) {
currentHintElement = listElement.children[0] as HTMLElement;
while (currentHintElement && isNormalItem(currentHintElement, className)) {
while (isAbnormalItem(currentHintElement, className)) {
currentHintElement = currentHintElement.nextElementSibling as HTMLElement;
}
}
@ -44,7 +44,7 @@ export const upDownHint = (listElement: Element, event: KeyboardEvent, classActi
currentHintElement.classList.remove(classActiveName);
currentHintElement = currentHintElement.previousElementSibling as HTMLElement;
while (currentHintElement && isNormalItem(currentHintElement, className)) {
while (isAbnormalItem(currentHintElement, className)) {
currentHintElement = currentHintElement.previousElementSibling as HTMLElement;
}
@ -70,7 +70,7 @@ export const upDownHint = (listElement: Element, event: KeyboardEvent, classActi
event.stopPropagation();
currentHintElement.classList.remove(classActiveName);
currentHintElement = listElement.children[0] as HTMLElement;
while (currentHintElement && isNormalItem(currentHintElement, className)) {
while (isAbnormalItem(currentHintElement, className)) {
currentHintElement = currentHintElement.nextElementSibling as HTMLElement;
}
if (!currentHintElement) {
@ -84,7 +84,7 @@ export const upDownHint = (listElement: Element, event: KeyboardEvent, classActi
event.stopPropagation();
currentHintElement.classList.remove(classActiveName);
currentHintElement = listElement.children[listElement.children.length - 1] as HTMLElement;
while (currentHintElement && isNormalItem(currentHintElement, className)) {
while (isAbnormalItem(currentHintElement, className)) {
currentHintElement = currentHintElement.previousElementSibling as HTMLElement;
}
if (!currentHintElement) {

File diff suppressed because one or more lines are too long

View file

@ -189,6 +189,20 @@ func getHeadingChildrenIDs(c *gin.Context) {
ret.Data = ids
}
func appendHeadingChildren(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
id := arg["id"].(string)
childrenDOM := arg["childrenDOM"].(string)
model.AppendHeadingChildren(id, childrenDOM)
}
func getHeadingChildrenDOM(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
@ -199,7 +213,11 @@ func getHeadingChildrenDOM(c *gin.Context) {
}
id := arg["id"].(string)
dom := model.GetHeadingChildrenDOM(id)
removeFoldAttr := true
if nil != arg["removeFoldAttr"] {
removeFoldAttr = arg["removeFoldAttr"].(bool)
}
dom := model.GetHeadingChildrenDOM(id, removeFoldAttr)
ret.Data = dom
}

View file

@ -35,9 +35,9 @@ func loadPetals(c *gin.Context) {
}
frontend := arg["frontend"].(string)
isPublish := model.IsReadOnlyRole(model.GetGinContextRole(c))
petals := model.LoadPetals(frontend)
ret.Data = petals
ret.Data = model.LoadPetals(frontend, isPublish)
}
func setPetalEnabled(c *gin.Context) {

View file

@ -222,6 +222,7 @@ func ServeAPI(ginServer *gin.Engine) {
ginServer.Handle("POST", "/api/block/getBlockRelevantIDs", model.CheckAuth, getBlockRelevantIDs)
ginServer.Handle("POST", "/api/block/getBlockTreeInfos", model.CheckAuth, getBlockTreeInfos)
ginServer.Handle("POST", "/api/block/checkBlockRef", model.CheckAuth, checkBlockRef)
ginServer.Handle("POST", "/api/block/appendHeadingChildren", model.CheckAuth, appendHeadingChildren)
ginServer.Handle("POST", "/api/file/getFile", model.CheckAuth, getFile)
ginServer.Handle("POST", "/api/file/putFile", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, putFile)

View file

@ -55,11 +55,20 @@ func getSnippet(c *gin.Context) {
return
}
isPublish := model.IsReadOnlyRole(model.GetGinContextRole(c))
var snippets []*conf.Snippet
for _, s := range confSnippets {
if ("all" == typ || s.Type == typ) && (2 == enabledArg || s.Enabled == enabled) {
snippets = append(snippets, s)
if isPublish && s.DisabledInPublish {
continue
}
if "all" != typ && s.Type != typ {
continue
}
if 2 != enabledArg && s.Enabled != enabled {
continue
}
snippets = append(snippets, s)
}
if "" != keyword {
@ -101,6 +110,9 @@ func setSnippet(c *gin.Context) {
Content: m["content"].(string),
Enabled: m["enabled"].(bool),
}
if nil != m["disabledInPublish"] {
snippet.DisabledInPublish = m["disabledInPublish"].(bool)
}
if "" == snippet.ID {
snippet.ID = ast.NewNodeID()
}

View file

@ -42,6 +42,8 @@ type AttributeView struct {
KeyIDs []string `json:"keyIDs"` // 属性视图属性键 ID用于排序
ViewID string `json:"viewID"` // 当前视图 ID
Views []*View `json:"views"` // 视图
RenderedViewables map[string]Viewable `json:"-"` // 已经渲染好的视图
}
// KeyValues 描述了属性视图属性键值列表的结构。
@ -428,7 +430,7 @@ func ParseAttributeView(avID string) (ret *AttributeView, err error) {
return
}
ret = &AttributeView{}
ret = &AttributeView{RenderedViewables: map[string]Viewable{}}
if err = gulu.JSON.UnmarshalJSON(data, ret); err != nil {
if strings.Contains(err.Error(), ".relation.contents of type av.Value") {
mapAv := map[string]interface{}{}

View file

@ -80,7 +80,7 @@ const (
type FilterQuantifier string
const (
FilterQuantifierUndefined FilterQuantifier = ""
FilterQuantifierUndefined FilterQuantifier = "" // 等同于 Any
FilterQuantifierAny FilterQuantifier = "Any"
FilterQuantifierAll FilterQuantifier = "All"
FilterQuantifierNone FilterQuantifier = "None"
@ -193,19 +193,8 @@ func (value *Value) Filter(filter *ViewFilter, attrView *AttributeView, itemID s
switch filter.Qualifier {
case FilterQuantifierUndefined, FilterQuantifierAny:
for _, content := range value.Rollup.Contents {
switch filter.Operator {
case FilterOperatorContains:
if content.filter(filter.Value.Rollup.Contents[0], filter.RelativeDate, filter.RelativeDate2, filter.Operator) {
return true
}
case FilterOperatorDoesNotContain:
if !content.filter(filter.Value.Rollup.Contents[0], filter.RelativeDate, filter.RelativeDate2, filter.Operator) {
return false
}
default:
if content.filter(filter.Value.Rollup.Contents[0], filter.RelativeDate, filter.RelativeDate2, filter.Operator) {
return true
}
if content.filter(filter.Value.Rollup.Contents[0], filter.RelativeDate, filter.RelativeDate2, filter.Operator) {
return true
}
}
case FilterQuantifierAll:

View file

@ -673,7 +673,7 @@ func Round(val float64, precision int) float64 {
type ValueSelect struct {
Content string `json:"content"`
Color string `json:"color"`
Color string `json:"color"` // 1-14
}
func MSelectExistOption(mSelect []*ValueSelect, opt string) bool {

View file

@ -51,7 +51,7 @@ type DisplayName struct {
ItIT string `json:"it_IT"`
JaJP string `json:"ja_JP"`
PlPL string `json:"pl_PL"`
ptBR string `json:"pt_BR"`
PtBR string `json:"pt_BR"`
RuRU string `json:"ru_RU"`
ZhCHT string `json:"zh_CHT"`
ZhCN string `json:"zh_CN"`
@ -68,7 +68,7 @@ type Description struct {
ItIT string `json:"it_IT"`
JaJP string `json:"ja_JP"`
PlPL string `json:"pl_PL"`
ptBR string `json:"pt_BR"`
PtBR string `json:"pt_BR"`
RuRU string `json:"ru_RU"`
ZhCHT string `json:"zh_CHT"`
ZhCN string `json:"zh_CN"`
@ -85,7 +85,7 @@ type Readme struct {
ItIT string `json:"it_IT"`
JaJP string `json:"ja_JP"`
PlPL string `json:"pl_PL"`
ptBR string `json:"pt_BR"`
PtBR string `json:"pt_BR"`
RuRU string `json:"ru_RU"`
ZhCHT string `json:"zh_CHT"`
ZhCN string `json:"zh_CN"`
@ -99,17 +99,18 @@ type Funding struct {
}
type Package struct {
Author string `json:"author"`
URL string `json:"url"`
Version string `json:"version"`
MinAppVersion string `json:"minAppVersion"`
Backends []string `json:"backends"`
Frontends []string `json:"frontends"`
DisplayName *DisplayName `json:"displayName"`
Description *Description `json:"description"`
Readme *Readme `json:"readme"`
Funding *Funding `json:"funding"`
Keywords []string `json:"keywords"`
Author string `json:"author"`
URL string `json:"url"`
Version string `json:"version"`
MinAppVersion string `json:"minAppVersion"`
DisabledInPublish bool `json:"disabledInPublish"`
Backends []string `json:"backends"`
Frontends []string `json:"frontends"`
DisplayName *DisplayName `json:"displayName"`
Description *Description `json:"description"`
Readme *Readme `json:"readme"`
Funding *Funding `json:"funding"`
Keywords []string `json:"keywords"`
PreferredFunding string `json:"preferredFunding"`
PreferredName string `json:"preferredName"`
@ -209,8 +210,8 @@ func getPreferredReadme(readme *Readme) string {
ret = readme.PlPL
}
case "pt_BR":
if "" != readme.ptBR {
ret = readme.ptBR
if "" != readme.PtBR {
ret = readme.PtBR
}
case "ru_RU":
if "" != readme.RuRU {
@ -279,8 +280,8 @@ func GetPreferredName(pkg *Package) string {
ret = pkg.DisplayName.PlPL
}
case "pt_BR":
if "" != pkg.DisplayName.ptBR {
ret = pkg.DisplayName.ptBR
if "" != pkg.DisplayName.PtBR {
ret = pkg.DisplayName.PtBR
}
case "ru_RU":
if "" != pkg.DisplayName.RuRU {
@ -349,8 +350,8 @@ func getPreferredDesc(desc *Description) string {
ret = desc.PlPL
}
case "pt_BR":
if "" != desc.ptBR {
ret = desc.ptBR
if "" != desc.PtBR {
ret = desc.PtBR
}
case "ru_RU":
if "" != desc.RuRU {
@ -875,9 +876,10 @@ func getBazaarIndex() map[string]*bazaarPackage {
const defaultMinAppVersion = "2.9.0"
func disallowDisplayBazaarPackage(pkg *Package) bool {
if "" == pkg.MinAppVersion { // TODO: 目前暂时放过所有不带 minAppVersion 的集市包,后续版本会使用 defaultMinAppVersion
return false
if "" == pkg.MinAppVersion {
pkg.MinAppVersion = defaultMinAppVersion
}
if 0 < semver.Compare("v"+pkg.MinAppVersion, "v"+util.Ver) {
return true
}

View file

@ -134,7 +134,7 @@ func Plugins(frontend string) (plugins []*Plugin) {
return
}
func ParseInstalledPlugin(name, frontend string) (found bool, displayName string, incompatible bool) {
func ParseInstalledPlugin(name, frontend string) (found bool, displayName string, incompatible, disabledInPublish bool) {
pluginsPath := filepath.Join(util.DataDir, "plugins")
if !util.IsPathRegularDirOrSymlinkDir(pluginsPath) {
return
@ -163,6 +163,7 @@ func ParseInstalledPlugin(name, frontend string) (found bool, displayName string
found = true
displayName = GetPreferredName(plugin.Package)
incompatible = isIncompatiblePlugin(plugin, frontend)
disabledInPublish = plugin.DisabledInPublish
}
return
}

View file

@ -29,9 +29,10 @@ func NewSnpt() *Snpt {
}
type Snippet struct {
ID string `json:"id"`
Name string `json:"name"`
Type string `json:"type"` // js/css
Enabled bool `json:"enabled"`
Content string `json:"content"`
ID string `json:"id"`
Name string `json:"name"`
Type string `json:"type"` // js/css
Enabled bool `json:"enabled"`
DisabledInPublish bool `json:"disabledInPublish"`
Content string `json:"content"`
}

View file

@ -8,7 +8,7 @@ require (
github.com/88250/epub v0.0.0-20230830085737-c19055cd1f48
github.com/88250/go-humanize v0.0.0-20240424102817-4f78fac47ea7
github.com/88250/gulu v1.2.3-0.20250227144607-7f4570b0d689
github.com/88250/lute v1.7.7-0.20250903134523-51a49dcdda40
github.com/88250/lute v1.7.7-0.20250907110109-efc34e9d52fa
github.com/88250/vitess-sqlparser v0.0.0-20210205111146-56a2ded2aba1
github.com/ClarkThan/ahocorasick v0.0.0-20231011042242-30d1ef1347f4
github.com/ConradIrwin/font v0.2.1

View file

@ -14,8 +14,8 @@ github.com/88250/go-sqlite3 v1.14.13-0.20231214121541-e7f54c482950 h1:Pa5hMiBceT
github.com/88250/go-sqlite3 v1.14.13-0.20231214121541-e7f54c482950/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/88250/gulu v1.2.3-0.20250227144607-7f4570b0d689 h1:39y5g7vnFAIcXhTN3IXPk7h2xBhC4a9hBTykDhHJqRY=
github.com/88250/gulu v1.2.3-0.20250227144607-7f4570b0d689/go.mod h1:c8uVw25vW2W4dhJ/j4iYsX5H1hc19spim266jO5x2hU=
github.com/88250/lute v1.7.7-0.20250903134523-51a49dcdda40 h1:PR2s/jxhLei+dk41Ogr+dhWtdqxbrpwiRQU6Odflnsc=
github.com/88250/lute v1.7.7-0.20250903134523-51a49dcdda40/go.mod h1:WYyUw//5yVw9BJnoVjx7rI/3szsISxNZCYGOqTIrV0o=
github.com/88250/lute v1.7.7-0.20250907110109-efc34e9d52fa h1:kbvW8LD3yJK5hwbLdJDb+7xqj7i68Rbz47xEqnNqf4I=
github.com/88250/lute v1.7.7-0.20250907110109-efc34e9d52fa/go.mod h1:WYyUw//5yVw9BJnoVjx7rI/3szsISxNZCYGOqTIrV0o=
github.com/88250/pdfcpu v0.3.14-0.20250424122812-f10e8d9d8d46 h1:Bq1JsDfVbHKUxNL/B2JXd8cC/1h6aFjrlXpGycnh0Hk=
github.com/88250/pdfcpu v0.3.14-0.20250424122812-f10e8d9d8d46/go.mod h1:fVfOloBzs2+W2VJCCbq60XIxc3yJHAZ0Gahv1oO0gyI=
github.com/88250/vitess-sqlparser v0.0.0-20210205111146-56a2ded2aba1 h1:48T899JQDwyyRu9yXHePYlPdHtpJfrJEUGBMH3SMBWY=

View file

@ -199,6 +199,7 @@ func getAttrViewAddingBlockDefaultValues(attrView *av.AttributeView, view, group
if nil != keyValues.Key.Date && keyValues.Key.Date.AutoFillNow {
newValue.Date.Content = time.Now().UnixMilli()
newValue.Date.IsNotEmpty = true
}
}
@ -270,6 +271,7 @@ func getAttrViewAddingBlockDefaultValues(attrView *av.AttributeView, view, group
if nil != keyValues.Key.Date && keyValues.Key.Date.AutoFillNow {
newValue.Date.Content = time.Now().UnixMilli()
newValue.Date.IsNotEmpty = true
}
return
}
@ -308,6 +310,7 @@ func getAttrViewAddingBlockDefaultValues(attrView *av.AttributeView, view, group
if nil != keyValues.Key.Date && keyValues.Key.Date.AutoFillNow {
newValue.Date.Content = time.Now().UnixMilli()
newValue.Date.IsNotEmpty = true
}
}
return
@ -1091,7 +1094,9 @@ func DuplicateDatabaseBlock(avID string) (newAvID, newBlockID string, err error)
return
}
newAv.Name = oldAv.Name + " (Duplicated " + time.Now().Format("2006-01-02 15:04:05") + ")"
if "" != newAv.Name {
newAv.Name = oldAv.Name + " (Duplicated " + time.Now().Format("2006-01-02 15:04:05") + ")"
}
for _, keyValues := range newAv.KeyValues {
if nil != keyValues.Key.Relation && keyValues.Key.Relation.IsTwoWay {

View file

@ -110,16 +110,9 @@ func renderAttributeViewGroups(viewable av.Viewable, attrView *av.AttributeView,
// 如果存在分组的话渲染分组视图
fixDev := false
for _, groupView := range view.Groups {
if (nil == groupView.GroupVal || nil == groupView.GroupKey) && !fixDev {
// TODO 分组上线后删除,预计 2025 年 9 月后可以删除
regenAttrViewGroups(attrView)
av.SaveAttributeView(attrView)
fixDev = true
}
switch groupView.GetGroupValue() {
groupView.Name = groupView.GetGroupValue()
switch groupView.Name {
case groupValueDefault:
groupView.Name = fmt.Sprintf(Conf.language(264), groupKey.Name)
case groupValueNotInRange:
@ -138,8 +131,6 @@ func renderAttributeViewGroups(viewable av.Viewable, attrView *av.AttributeView,
groupView.Name = fmt.Sprintf(Conf.language(263), 7)
case groupValueNext30Days:
groupView.Name = fmt.Sprintf(Conf.language(263), 30)
default:
groupView.Name = groupView.GetGroupValue()
}
}
@ -484,7 +475,7 @@ func RenderRepoSnapshotAttributeView(indexID, avID string) (viewable av.Viewable
return
}
attrView = &av.AttributeView{}
attrView = &av.AttributeView{RenderedViewables: map[string]av.Viewable{}}
if err = gulu.JSON.UnmarshalJSON(data, attrView); err != nil {
logging.LogErrorf("unmarshal attribute view [%s] failed: %s", avID, err)
return
@ -527,7 +518,7 @@ func RenderHistoryAttributeView(avID, created string) (viewable av.Viewable, att
return
}
attrView = &av.AttributeView{}
attrView = &av.AttributeView{RenderedViewables: map[string]av.Viewable{}}
if err = gulu.JSON.UnmarshalJSON(data, attrView); err != nil {
logging.LogErrorf("unmarshal attribute view [%s] failed: %s", avID, err)
return

View file

@ -20,6 +20,7 @@ import (
"bytes"
"errors"
"fmt"
"slices"
"strings"
"time"
@ -629,7 +630,35 @@ func GetHeadingChildrenIDs(id string) (ret []string) {
return
}
func GetHeadingChildrenDOM(id string) (ret string) {
func AppendHeadingChildren(id, childrenDOM string) {
tree, err := LoadTreeByBlockID(id)
if err != nil {
return
}
heading := treenode.GetNodeInTree(tree, id)
if nil == heading || ast.NodeHeading != heading.Type {
return
}
luteEngine := util.NewLute()
subTree := luteEngine.BlockDOM2Tree(childrenDOM)
var nodes []*ast.Node
for n := subTree.Root.FirstChild; nil != n; n = n.Next {
nodes = append(nodes, n)
}
slices.Reverse(nodes)
for _, n := range nodes {
heading.InsertAfter(n)
}
if err = indexWriteTreeUpsertQueue(tree); err != nil {
return
}
}
func GetHeadingChildrenDOM(id string, removeFoldAttr bool) (ret string) {
tree, err := LoadTreeByBlockID(id)
if err != nil {
return
@ -643,20 +672,26 @@ func GetHeadingChildrenDOM(id string) (ret string) {
children := treenode.HeadingChildren(heading)
nodes = append(nodes, children...)
// 取消折叠 https://github.com/siyuan-note/siyuan/issues/13232#issuecomment-2535955152
for _, child := range children {
ast.Walk(child, func(n *ast.Node, entering bool) ast.WalkStatus {
if !entering {
return ast.WalkContinue
}
n.RemoveIALAttr("heading-fold")
n.RemoveIALAttr("fold")
if removeFoldAttr {
n.RemoveIALAttr("heading-fold")
n.RemoveIALAttr("fold")
}
return ast.WalkContinue
})
child.SetIALAttr("parent-heading", id)
}
if removeFoldAttr {
heading.RemoveIALAttr("fold")
heading.RemoveIALAttr("heading-fold")
}
heading.RemoveIALAttr("fold")
heading.RemoveIALAttr("heading-fold")
luteEngine := util.NewLute()
ret = renderBlockDOMByNodes(nodes, luteEngine)

View file

@ -187,7 +187,7 @@ func SearchDocsByKeyword(keyword string, flashcard bool) (ret []map[string]strin
}
}
rootBlocks = sql.QueryRootBlockByCondition(condition)
rootBlocks = sql.QueryRootBlockByCondition(condition, Conf.Search.Limit)
} else {
for _, box := range boxes {
if flashcard {

View file

@ -203,6 +203,12 @@ func Mount(boxID string) (alreadyMount bool, err error) {
return
}
boxes, _ := ListNotebooks()
var sort int
if len(boxes) > 0 {
sort = boxes[0].Sort - 1
}
p := filepath.Join(util.WorkingDir, "guide", boxID)
if err = filelock.Copy(p, localPath); err != nil {
return
@ -218,6 +224,7 @@ func Mount(boxID string) (alreadyMount bool, err error) {
if box := Conf.Box(boxID); nil != box {
boxConf := box.GetConf()
boxConf.Closed = true
boxConf.Sort = sort
box.SaveConf(boxConf)
}

View file

@ -31,10 +31,11 @@ import (
// Petal represents a plugin's management status.
type Petal struct {
Name string `json:"name"` // Plugin name
DisplayName string `json:"displayName"` // Plugin display name
Enabled bool `json:"enabled"` // Whether enabled
Incompatible bool `json:"incompatible"` // Whether incompatible
Name string `json:"name"` // Plugin name
DisplayName string `json:"displayName"` // Plugin display name
Enabled bool `json:"enabled"` // Whether enabled
Incompatible bool `json:"incompatible"` // Whether incompatible
DisabledInPublish bool `json:"disabledInPublish"` // Whether disabled in publish mode
JS string `json:"js"` // JS code
CSS string `json:"css"` // CSS code
@ -44,7 +45,7 @@ type Petal struct {
func SetPetalEnabled(name string, enabled bool, frontend string) (ret *Petal, err error) {
petals := getPetals()
found, displayName, incompatible := bazaar.ParseInstalledPlugin(name, frontend)
found, displayName, incompatible, disabledInPublish := bazaar.ParseInstalledPlugin(name, frontend)
if !found {
logging.LogErrorf("plugin [%s] not found", name)
return
@ -60,6 +61,7 @@ func SetPetalEnabled(name string, enabled bool, frontend string) (ret *Petal, er
ret.DisplayName = displayName
ret.Enabled = enabled
ret.Incompatible = incompatible
ret.DisabledInPublish = disabledInPublish
if incompatible {
err = fmt.Errorf(Conf.Language(205))
@ -72,7 +74,7 @@ func SetPetalEnabled(name string, enabled bool, frontend string) (ret *Petal, er
return
}
func LoadPetals(frontend string) (ret []*Petal) {
func LoadPetals(frontend string, isPublish bool) (ret []*Petal) {
ret = []*Petal{}
if Conf.Bazaar.PetalDisabled {
@ -93,8 +95,8 @@ func LoadPetals(frontend string) (ret []*Petal) {
continue
}
_, petal.DisplayName, petal.Incompatible = bazaar.ParseInstalledPlugin(petal.Name, frontend)
if !petal.Enabled || petal.Incompatible {
_, petal.DisplayName, petal.Incompatible, petal.DisabledInPublish = bazaar.ParseInstalledPlugin(petal.Name, frontend)
if !petal.Enabled || petal.Incompatible || (isPublish && petal.DisabledInPublish) {
continue
}

View file

@ -587,20 +587,20 @@ func (tx *Transaction) doPrependInsert(operation *Operation) (ret *TxErr) {
if nil == insertedNode {
return &TxErr{code: TxErrCodeBlockNotFound, msg: "invalid data tree", id: block.ID}
}
var remains []*ast.Node
for remain := insertedNode.Next; nil != remain; remain = remain.Next {
if ast.NodeKramdownBlockIAL != remain.Type {
if "" == remain.ID {
remain.ID = ast.NewNodeID()
remain.SetIALAttr("id", remain.ID)
}
remains = append(remains, remain)
}
}
if "" == insertedNode.ID {
insertedNode.ID = ast.NewNodeID()
insertedNode.SetIALAttr("id", insertedNode.ID)
}
var toInserts []*ast.Node
for toInsert := insertedNode; nil != toInsert; toInsert = toInsert.Next {
if ast.NodeKramdownBlockIAL != toInsert.Type {
if "" == toInsert.ID {
toInsert.ID = ast.NewNodeID()
toInsert.SetIALAttr("id", toInsert.ID)
}
toInserts = append(toInserts, toInsert)
}
}
node := treenode.GetNodeInTree(tree, operation.ParentID)
if nil == node {
@ -608,31 +608,45 @@ func (tx *Transaction) doPrependInsert(operation *Operation) (ret *TxErr) {
return &TxErr{code: TxErrCodeBlockNotFound, id: operation.ParentID}
}
isContainer := node.IsContainerBlock()
for i := len(remains) - 1; 0 <= i; i-- {
remain := remains[i]
slices.Reverse(toInserts)
for _, toInsert := range toInserts {
if isContainer {
if ast.NodeListItem == node.Type && 3 == node.ListData.Typ {
node.FirstChild.InsertAfter(remain)
if ast.NodeList == node.Type {
// 列表下只能挂列表项,所以这里需要分情况处理
if ast.NodeList == toInsert.Type {
var childLis []*ast.Node
for childLi := toInsert.FirstChild; nil != childLi; childLi = childLi.Next {
childLis = append(childLis, childLi)
}
for i := len(childLis) - 1; -1 < i; i-- {
node.PrependChild(childLis[i])
}
} else {
newLiID := ast.NewNodeID()
newLi := &ast.Node{ID: newLiID, Type: ast.NodeListItem, ListData: &ast.ListData{Typ: node.ListData.Typ}}
newLi.SetIALAttr("id", newLiID)
node.PrependChild(newLi)
newLi.AppendChild(toInsert)
}
} else if ast.NodeSuperBlock == node.Type {
node.FirstChild.Next.InsertAfter(remain)
layout := node.ChildByType(ast.NodeSuperBlockLayoutMarker)
if nil != layout {
layout.InsertAfter(toInsert)
} else {
node.FirstChild.InsertAfter(toInsert)
}
} else {
node.PrependChild(remain)
node.PrependChild(toInsert)
}
} else {
node.InsertAfter(remain)
node.InsertAfter(toInsert)
}
createdUpdated(toInsert)
tx.nodes[toInsert.ID] = toInsert
}
if isContainer {
if ast.NodeListItem == node.Type && 3 == node.ListData.Typ {
node.FirstChild.InsertAfter(insertedNode)
} else if ast.NodeSuperBlock == node.Type {
node.FirstChild.Next.InsertAfter(insertedNode)
} else {
node.PrependChild(insertedNode)
}
} else {
node.InsertAfter(insertedNode)
}
createdUpdated(insertedNode)
tx.nodes[insertedNode.ID] = insertedNode
if err = tx.writeTree(tree); err != nil {
@ -692,8 +706,17 @@ func (tx *Transaction) doAppendInsert(operation *Operation) (ret *TxErr) {
return &TxErr{code: TxErrCodeBlockNotFound, id: operation.ParentID}
}
isContainer := node.IsContainerBlock()
for i := 0; i < len(toInserts); i++ {
toInsert := toInserts[i]
if !isContainer {
slices.Reverse(toInserts)
}
var lastChildBelowHeading *ast.Node
if ast.NodeHeading == node.Type {
if children := treenode.HeadingChildren(node); 0 < len(children) {
lastChildBelowHeading = children[len(children)-1]
}
}
for _, toInsert := range toInserts {
if isContainer {
if ast.NodeList == node.Type {
// 列表下只能挂列表项,所以这里需要分情况处理 https://github.com/siyuan-note/siyuan/issues/9955
@ -718,8 +741,19 @@ func (tx *Transaction) doAppendInsert(operation *Operation) (ret *TxErr) {
node.AppendChild(toInsert)
}
} else {
node.InsertAfter(toInsert)
if ast.NodeHeading == node.Type {
if nil != lastChildBelowHeading {
lastChildBelowHeading.InsertAfter(toInsert)
} else {
node.InsertAfter(toInsert)
}
} else {
node.InsertAfter(toInsert)
}
}
createdUpdated(toInsert)
tx.nodes[toInsert.ID] = toInsert
}
createdUpdated(insertedNode)
@ -1248,11 +1282,19 @@ func (tx *Transaction) doInsert(operation *Operation) (ret *TxErr) {
node.FirstChild.InsertAfter(remain)
}
} else {
for i := len(remains) - 1; 0 <= i; i-- {
remain := remains[i]
node.PrependChild(remain)
if !node.IsContainerBlock() {
for i := len(remains) - 1; 0 <= i; i-- {
remain := remains[i]
node.InsertAfter(remain)
}
node.InsertAfter(insertedNode)
} else {
for i := len(remains) - 1; 0 <= i; i-- {
remain := remains[i]
node.PrependChild(remain)
}
node.PrependChild(insertedNode)
}
node.PrependChild(insertedNode)
}
}
}

View file

@ -43,7 +43,7 @@ import (
func resetTree(tree *parse.Tree, titleSuffix string, removeAvBinding bool) {
tree.ID = ast.NewNodeID()
tree.Root.ID = tree.ID
title := tree.Root.IALAttr("title")
if "" != titleSuffix {
if t, parseErr := time.Parse("20060102150405", util.TimeFromID(tree.ID)); nil == parseErr {
titleSuffix += " " + t.Format("2006-01-02 15:04:05")
@ -52,9 +52,12 @@ func resetTree(tree *parse.Tree, titleSuffix string, removeAvBinding bool) {
}
titleSuffix = "(" + titleSuffix + ")"
titleSuffix = " " + titleSuffix
if Conf.language(16) == title {
titleSuffix = ""
}
}
tree.Root.SetIALAttr("id", tree.ID)
tree.Root.SetIALAttr("title", tree.Root.IALAttr("title")+titleSuffix)
tree.Root.SetIALAttr("title", title+titleSuffix)
tree.Root.RemoveIALAttr("scroll")
p := path.Join(path.Dir(tree.Path), tree.ID) + ".sy"
tree.Path = p

View file

@ -74,11 +74,13 @@ func RenderView(attrView *av.AttributeView, view *av.View, query string) (ret av
renderedAttrViews := map[string]*av.AttributeView{}
renderedAttrViews[attrView.ID] = attrView
ret = renderView(attrView, view, query, &depth, renderedAttrViews)
attrView.RenderedViewables[ret.GetID()] = ret
renderedAttrViews[attrView.ID] = attrView
return
}
func renderView(attrView *av.AttributeView, view *av.View, query string,
depth *int, cachedAttrViews map[string]*av.AttributeView) (ret av.Viewable) {
func renderView(attrView *av.AttributeView, view *av.View, query string, depth *int, cachedAttrViews map[string]*av.AttributeView) (ret av.Viewable) {
if 7 < *depth {
return
}
@ -334,9 +336,7 @@ func fillAttributeViewBaseValue(baseValue *av.BaseValue, fieldID, itemID string,
}
}
func fillAttributeViewAutoGeneratedValues(attrView *av.AttributeView, collection av.Collection, ials map[string]map[string]string,
depth *int, cachedAttrViews map[string]*av.AttributeView) {
func fillAttributeViewAutoGeneratedValues(attrView *av.AttributeView, collection av.Collection, ials map[string]map[string]string, depth *int, cachedAttrViews map[string]*av.AttributeView) {
// 先渲染主键、创建时间、更新时间
for _, item := range collection.GetItems() {
@ -630,23 +630,6 @@ func fillAttributeViewKeyValues(attrView *av.AttributeView, collection av.Collec
}
}
func mergeKeyValues(kv1, kv2 []*av.KeyValues) (ret []*av.KeyValues) {
ret = kv2
for _, k1 := range kv1 {
found := false
for _, k2 := range kv2 {
if k1.Key.ID == k2.Key.ID {
found = true
break
}
}
if !found {
ret = append(ret, k1)
}
}
return
}
func FillAttributeViewNilValue(value *av.Value, typ av.KeyType) {
value.Type = typ
switch typ {

View file

@ -18,8 +18,13 @@ import (
"github.com/siyuan-note/siyuan/kernel/util"
)
func RenderAttributeViewGallery(attrView *av.AttributeView, view *av.View, query string,
depth *int, cachedAttrViews map[string]*av.AttributeView) (ret *av.Gallery) {
func RenderAttributeViewGallery(attrView *av.AttributeView, view *av.View, query string, depth *int, cachedAttrViews map[string]*av.AttributeView) (ret *av.Gallery) {
viewable := attrView.RenderedViewables[view.ID]
if nil != viewable {
ret = viewable.(*av.Gallery)
return
}
ret = &av.Gallery{
BaseInstance: av.NewViewBaseInstance(view),
CoverFrom: view.Gallery.CoverFrom,

View file

@ -22,8 +22,13 @@ import (
"github.com/siyuan-note/siyuan/kernel/util"
)
func RenderAttributeViewTable(attrView *av.AttributeView, view *av.View, query string,
depth *int, cachedAttrViews map[string]*av.AttributeView) (ret *av.Table) {
func RenderAttributeViewTable(attrView *av.AttributeView, view *av.View, query string, depth *int, cachedAttrViews map[string]*av.AttributeView) (ret *av.Table) {
viewable := attrView.RenderedViewables[view.ID]
if nil != viewable {
ret = viewable.(*av.Table)
return
}
ret = &av.Table{
BaseInstance: av.NewViewBaseInstance(view),
Columns: []*av.TableColumn{},

View file

@ -69,8 +69,8 @@ func queryBlockHashes(rootID string) (ret map[string]string) {
return
}
func QueryRootBlockByCondition(condition string) (ret []*Block) {
sqlStmt := "SELECT *, length(hpath) - length(replace(hpath, '/', '')) AS lv FROM blocks WHERE type = 'd' AND " + condition + " ORDER BY box DESC,lv ASC LIMIT 128"
func QueryRootBlockByCondition(condition string, limit int) (ret []*Block) {
sqlStmt := "SELECT *, length(hpath) - length(replace(hpath, '/', '')) AS lv FROM blocks WHERE type = 'd' AND " + condition + " ORDER BY box DESC,lv ASC LIMIT " + strconv.Itoa(limit)
rows, err := query(sqlStmt)
if err != nil {
logging.LogErrorf("sql query [%s] failed: %s", sqlStmt, err)

View file

@ -34,6 +34,10 @@ func init() {
}
func GetDuplicateName(master string) (ret string) {
if "" == master {
return
}
ret = master + " (1)"
r := regexp.MustCompile("^(.*) \\((\\d+)\\)$")
m := r.FindStringSubmatch(master)

View file

@ -45,7 +45,7 @@ import (
var Mode = "prod"
const (
Ver = "3.3.1"
Ver = "3.3.2"
IsInsider = false
// env vars as fallback for commandline parameters

View file

@ -4,6 +4,9 @@ echo 'use ".\scripts\win-build.bat" instead of "win-build.bat"'
echo 'Building UI'
cd app
call pnpm install
if errorlevel 1 (
exit /b %errorlevel%
)
call pnpm run build
if errorlevel 1 (
exit /b %errorlevel%