mirror of
https://github.com/siyuan-note/siyuan.git
synced 2025-09-22 00:20:47 +02:00
Compare commits
87 commits
v3.3.2-dev
...
master
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f1c91863b9 | ||
![]() |
8a9e746891 | ||
![]() |
517f5c8453 | ||
![]() |
88431279bf | ||
![]() |
dc656d83a2 | ||
![]() |
b69fd04137 | ||
![]() |
ff909fa149 | ||
![]() |
bfe50d9009 | ||
![]() |
93422c134d | ||
![]() |
2f70ef43a1 | ||
![]() |
5f6ddb4655 | ||
![]() |
39c4b3325e | ||
![]() |
393c53941a | ||
![]() |
a4f03191fa | ||
![]() |
de9e648e9e | ||
![]() |
c1f14a33d9 | ||
![]() |
adca241ed5 | ||
![]() |
006da6bc90 | ||
![]() |
490234caab | ||
![]() |
80c357564d | ||
![]() |
6852a49620 | ||
![]() |
93591ad44e | ||
![]() |
6510d7dbf0 | ||
![]() |
29244a1f8c | ||
![]() |
a85467751f | ||
![]() |
14251d8dae | ||
![]() |
52a4815419 | ||
![]() |
45a6a190d0 | ||
![]() |
c1a4aa3128 | ||
![]() |
0fce405a05 | ||
![]() |
fe143bcb12 | ||
![]() |
0ad7c4cf23 | ||
![]() |
c326989391 | ||
![]() |
468b670bcc | ||
![]() |
4e4398ef47 | ||
![]() |
fc1cbf46aa | ||
![]() |
80cccd41b5 | ||
![]() |
68991a6aef | ||
![]() |
2a8b47b518 | ||
![]() |
8ad3cb00ad | ||
![]() |
c55c413365 | ||
![]() |
4767e399e2 | ||
![]() |
5ade06a3b6 | ||
![]() |
8ebb617072 | ||
![]() |
305bd9dfb0 | ||
![]() |
d2f990a830 | ||
![]() |
5775aa9734 | ||
![]() |
80f1c6c3ec | ||
![]() |
f220e3627f | ||
![]() |
1fb4ed980b | ||
![]() |
04bf9e9848 | ||
![]() |
9447f1c5a8 | ||
![]() |
afa74ebb4b | ||
![]() |
17a96ee1b1 | ||
![]() |
647204eee1 | ||
![]() |
4eb91f2e40 | ||
![]() |
d48e3d5211 | ||
![]() |
59265bfc94 | ||
![]() |
16913fb065 | ||
![]() |
65b52a2bc6 | ||
![]() |
ac6b1d6689 | ||
![]() |
7ae90058c3 | ||
![]() |
fdfd453e2b | ||
![]() |
0aa5624495 | ||
![]() |
0b3cef8964 | ||
![]() |
2c5d4d47a4 | ||
![]() |
3379d29e67 | ||
![]() |
ba95f45b92 | ||
![]() |
ed78e67af2 | ||
![]() |
ba10d96a10 | ||
![]() |
4d8bc1672c | ||
![]() |
e91a37a98b | ||
![]() |
f6bd240a85 | ||
![]() |
c08f88156f | ||
![]() |
74826fc0ce | ||
![]() |
e1bb9874be | ||
![]() |
2b85cb1b6c | ||
![]() |
cd8c3a41e6 | ||
![]() |
bdace41d97 | ||
![]() |
ea2294e901 | ||
![]() |
868c9d980a | ||
![]() |
2d53c81026 | ||
![]() |
6ff4439be3 | ||
![]() |
d47a8ffe02 | ||
![]() |
9410a70a2b | ||
![]() |
3c3f34442f | ||
![]() |
9dc6f56bff |
83 changed files with 4414 additions and 3191 deletions
2
.github/CONTRIBUTING.md
vendored
2
.github/CONTRIBUTING.md
vendored
|
@ -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>
|
||||
|
|
2
.github/CONTRIBUTING_zh_CN.md
vendored
2
.github/CONTRIBUTING_zh_CN.md
vendored
|
@ -7,7 +7,7 @@
|
|||
|
||||
## NPM 依赖
|
||||
|
||||
安装 pnpm:`npm install -g pnpm@10.15.0`
|
||||
安装 pnpm:`npm install -g pnpm@10.15.1`
|
||||
|
||||
<details>
|
||||
<summary>适用于中国大陆</summary>
|
||||
|
|
|
@ -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": "إغلاق جميع علامات التبويب عند بدء التشغيل",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 today’s 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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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, l’icô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",
|
||||
|
|
|
@ -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": "סגור את כל הטאבים בעת אתחול",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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": "起動時にすべてのタブを閉じる",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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": "Закрыть все вкладки при запуске",
|
||||
|
|
|
@ -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": "啟動時關閉所有分頁",
|
||||
|
|
|
@ -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": "启动时关闭所有页签",
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
|
|
48
app/changelogs/v3.3.2/v3.3.2.md
Normal file
48
app/changelogs/v3.3.2/v3.3.2.md
Normal 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)
|
48
app/changelogs/v3.3.2/v3.3.2_zh_CHT.md
Normal file
48
app/changelogs/v3.3.2/v3.3.2_zh_CHT.md
Normal 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)
|
48
app/changelogs/v3.3.2/v3.3.2_zh_CN.md
Normal file
48
app/changelogs/v3.3.2/v3.3.2_zh_CN.md
Normal 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)
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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."
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -3818,7 +3818,7 @@
|
|||
"Children": [
|
||||
{
|
||||
"Type": "NodeText",
|
||||
"Data": "代码块中使用时仅选中代码块中的内容"
|
||||
"Data": "连续按下两次以选中文档中所有已加载的内容块;代码块中使用时仅选中代码块中的内容"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -3808,7 +3808,7 @@
|
|||
"Children": [
|
||||
{
|
||||
"Type": "NodeText",
|
||||
"Data": "代碼塊中使用時僅選中代碼塊中的內容"
|
||||
"Data": "連續按下兩次以選中文檔中所有已加載的內容塊;代碼塊中使用時僅選中代碼塊中的內容"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -3775,7 +3775,7 @@
|
|||
"Children": [
|
||||
{
|
||||
"Type": "NodeText",
|
||||
"Data": "コードブロックではコードのみが選択されます"
|
||||
"Data": "文書内のすべてのロード済みコンテンツブロックを選択するにはダブルプレスしてください;コードブロックではコードのみが選択されます"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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
6284
app/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
@ -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) {
|
||||
|
|
|
@ -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) &&
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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});
|
||||
|
|
|
@ -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({
|
||||
|
@ -1008,7 +1028,9 @@ export const showColMenu = (protyle: IProtyle, blockElement: Element, cellElemen
|
|||
${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("${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>
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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">${selectHTML}</select>`
|
||||
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" 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}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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,11 +511,13 @@ export const insertHTML = (html: string, protyle: IProtyle, isBlock = false,
|
|||
id: addId,
|
||||
});
|
||||
}
|
||||
if (!hasParentHeading) {
|
||||
if (insertBefore) {
|
||||
blockElement.before(item);
|
||||
} else {
|
||||
blockElement.after(item);
|
||||
}
|
||||
}
|
||||
if (!lastElement) {
|
||||
lastElement = item;
|
||||
}
|
||||
|
|
|
@ -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", {
|
||||
|
|
|
@ -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 {
|
||||
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 数据
|
||||
if (!isInCodeBlock) {
|
||||
enableLuteMarkdownSyntax(protyle);
|
||||
const siyuanHTML = selectTableElement ? protyle.lute.HTML2BlockDOM(html) : html;
|
||||
event.clipboardData.setData("text/siyuan", siyuanHTML);
|
||||
const textSiyuan = selectTableElement ? protyle.lute.HTML2BlockDOM(html) : html;
|
||||
event.clipboardData.setData("text/siyuan", textSiyuan);
|
||||
restoreLuteMarkdownSyntax(protyle);
|
||||
|
||||
// 在 text/html 中插入注释节点,用于右键菜单粘贴时获取 text/siyuan 数据
|
||||
event.clipboardData.setData("text/html", `<!--data-siyuan='${encodeBase64(siyuanHTML)}'-->` + (selectTableElement ? html : protyle.lute.BlockDOM2HTML(selectAVElement ? textPlain : html)));
|
||||
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 数据
|
||||
if (!isInCodeBlock) {
|
||||
enableLuteMarkdownSyntax(protyle);
|
||||
const siyuanHTML = selectTableElement ? protyle.lute.HTML2BlockDOM(html) : html;
|
||||
event.clipboardData.setData("text/siyuan", siyuanHTML);
|
||||
const textSiyuan = selectTableElement ? protyle.lute.HTML2BlockDOM(html) : html;
|
||||
restoreLuteMarkdownSyntax(protyle);
|
||||
|
||||
event.clipboardData.setData("text/siyuan", textSiyuan);
|
||||
// 在 text/html 中插入注释节点,用于右键菜单粘贴时获取 text/siyuan 数据
|
||||
event.clipboardData.setData("text/html", `<!--data-siyuan='${encodeBase64(siyuanHTML)}'-->` + (selectTableElement ? html : protyle.lute.BlockDOM2HTML(selectAVElement ? textPlain : html)));
|
||||
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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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}"]`);
|
||||
|
|
14
app/src/types/config.d.ts
vendored
14
app/src/types/config.d.ts
vendored
|
@ -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;
|
||||
/**
|
||||
|
|
2
app/src/types/index.d.ts
vendored
2
app/src/types/index.d.ts
vendored
|
@ -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
|
||||
|
|
|
@ -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})`;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
2
app/stage/protyle/js/lute/lute.min.js
vendored
2
app/stage/protyle/js/lute/lute.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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{}{}
|
||||
|
|
|
@ -80,7 +80,7 @@ const (
|
|||
type FilterQuantifier string
|
||||
|
||||
const (
|
||||
FilterQuantifierUndefined FilterQuantifier = ""
|
||||
FilterQuantifierUndefined FilterQuantifier = "" // 等同于 Any
|
||||
FilterQuantifierAny FilterQuantifier = "Any"
|
||||
FilterQuantifierAll FilterQuantifier = "All"
|
||||
FilterQuantifierNone FilterQuantifier = "None"
|
||||
|
@ -193,20 +193,9 @@ 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
|
||||
}
|
||||
}
|
||||
}
|
||||
case FilterQuantifierAll:
|
||||
for _, content := range value.Rollup.Contents {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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"`
|
||||
|
@ -103,6 +103,7 @@ type Package struct {
|
|||
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"`
|
||||
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -33,5 +33,6 @@ type Snippet struct {
|
|||
Name string `json:"name"`
|
||||
Type string `json:"type"` // js/css
|
||||
Enabled bool `json:"enabled"`
|
||||
DisabledInPublish bool `json:"disabledInPublish"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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=
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
luteEngine := util.NewLute()
|
||||
ret = renderBlockDOMByNodes(nodes, luteEngine)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ type Petal struct {
|
|||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -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.PrependChild(remain)
|
||||
node.FirstChild.InsertAfter(toInsert)
|
||||
}
|
||||
} else {
|
||||
node.InsertAfter(remain)
|
||||
}
|
||||
}
|
||||
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)
|
||||
node.PrependChild(toInsert)
|
||||
}
|
||||
} else {
|
||||
node.InsertAfter(insertedNode)
|
||||
node.InsertAfter(toInsert)
|
||||
}
|
||||
|
||||
createdUpdated(toInsert)
|
||||
tx.nodes[toInsert.ID] = toInsert
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -717,9 +740,20 @@ func (tx *Transaction) doAppendInsert(operation *Operation) (ret *TxErr) {
|
|||
} else {
|
||||
node.AppendChild(toInsert)
|
||||
}
|
||||
} else {
|
||||
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)
|
||||
|
@ -1247,6 +1281,13 @@ func (tx *Transaction) doInsert(operation *Operation) (ret *TxErr) {
|
|||
for _, remain := range remains {
|
||||
node.FirstChild.InsertAfter(remain)
|
||||
}
|
||||
} else {
|
||||
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]
|
||||
|
@ -1256,6 +1297,7 @@ func (tx *Transaction) doInsert(operation *Operation) (ret *TxErr) {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
createdUpdated(insertedNode)
|
||||
tx.nodes[insertedNode.ID] = insertedNode
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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{},
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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%
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue