From cd115ad53f8644f184922d3147eee2249711dc95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A2=96=E9=80=B8?= <49649786+Zuoqiu-Yingyi@users.noreply.github.com> Date: Sat, 14 Oct 2023 22:27:55 +0800 Subject: [PATCH] Merge tag 'v2.10.11-dev2' into feat/attrs-view-adaptive-width --- app/appearance/icons/ant/icon.js | 3 + app/appearance/icons/ant/icon.json | 2 +- app/appearance/icons/index.html | 6 + app/appearance/icons/material/icon.js | 3 + app/appearance/icons/material/icon.json | 2 +- app/appearance/langs/en_US.json | 2 +- app/appearance/langs/es_ES.json | 2 +- app/appearance/langs/fr_FR.json | 2 +- app/appearance/langs/zh_CHT.json | 2 +- app/appearance/langs/zh_CN.json | 2 +- app/appx/AppxManifest.xml | 2 +- app/electron/main.js | 5 +- .../20221223221636-ms2b4w9.sy | 38 +- .../20230405172236-pg3l9eu.sy | 10 +- .../20221223215557-o6gfsoy.sy | 38 +- .../20230405155631-leo4vc6.sy | 31 +- .../20221223221501-mops33i.sy | 32 +- .../20230405172131-yb16aax.sy | 28 +- app/src/assets/icon-mac.png | Bin 0 -> 28687 bytes app/src/assets/icon.icns | Bin 26959 -> 90968 bytes app/src/assets/scss/base.scss | 4 +- app/src/assets/scss/business/_av.scss | 24 +- .../assets/scss/component/_typography.scss | 2 +- app/src/assets/scss/protyle/_toolbar.scss | 2 +- app/src/config/exportConfig.ts | 2 +- app/src/layout/status.ts | 3 + app/src/menus/protyle.ts | 11 +- app/src/protyle/gutter/index.ts | 40 ++- app/src/protyle/index.ts | 4 +- app/src/protyle/render/av/action.ts | 141 ++++---- app/src/protyle/render/av/asset.ts | 4 +- app/src/protyle/render/av/blockAttr.ts | 3 + app/src/protyle/render/av/cell.ts | 185 +++++++--- app/src/protyle/render/av/col.ts | 43 +-- app/src/protyle/render/av/filter.ts | 17 +- app/src/protyle/render/av/keydown.ts | 140 ++++++++ app/src/protyle/render/av/openMenuPanel.ts | 22 +- app/src/protyle/render/av/render.ts | 94 +++-- app/src/protyle/render/av/row.ts | 76 ++++ app/src/protyle/render/av/sort.ts | 2 +- app/src/protyle/toolbar/index.ts | 2 +- app/src/protyle/util/editorCommonEvent.ts | 2 +- app/src/protyle/util/resize.ts | 33 ++ app/src/protyle/wysiwyg/index.ts | 24 +- app/src/protyle/wysiwyg/keydown.ts | 31 +- app/src/protyle/wysiwyg/transaction.ts | 4 + app/src/types/index.d.ts | 1 + kernel/api/transaction.go | 6 +- kernel/av/av.go | 10 + kernel/av/table.go | 160 ++++++++- kernel/model/attribute_view.go | 155 ++++---- kernel/model/blockinfo.go | 2 + kernel/model/plugin.go | 2 +- kernel/model/transaction.go | 30 ++ kernel/treenode/node.go | 339 +++++++++++++++++- kernel/util/working.go | 6 +- scripts/win-build.bat | 11 +- 57 files changed, 1462 insertions(+), 385 deletions(-) create mode 100644 app/src/assets/icon-mac.png create mode 100644 app/src/protyle/render/av/keydown.ts create mode 100644 app/src/protyle/render/av/row.ts diff --git a/app/appearance/icons/ant/icon.js b/app/appearance/icons/ant/icon.js index 96b19ac8da2..d7b227455df 100644 --- a/app/appearance/icons/ant/icon.js +++ b/app/appearance/icons/ant/icon.js @@ -1,5 +1,8 @@ document.body.insertAdjacentHTML('afterbegin', ` + + + diff --git a/app/appearance/icons/ant/icon.json b/app/appearance/icons/ant/icon.json index 330897d63ca..8737d32b7d6 100644 --- a/app/appearance/icons/ant/icon.json +++ b/app/appearance/icons/ant/icon.json @@ -2,5 +2,5 @@ "name": "ant", "author": "Vanessa", "url": "https://github.com/Vanessa219", - "version": "1.24.0" + "version": "1.25.0" } diff --git a/app/appearance/icons/index.html b/app/appearance/icons/index.html index 44710953266..19549de140d 100644 --- a/app/appearance/icons/index.html +++ b/app/appearance/icons/index.html @@ -28,6 +28,12 @@

SiYuan

+
+ + + + iconKey +
diff --git a/app/appearance/icons/material/icon.js b/app/appearance/icons/material/icon.js index 4bb6b75d8a0..80976c10eb3 100644 --- a/app/appearance/icons/material/icon.js +++ b/app/appearance/icons/material/icon.js @@ -1,5 +1,8 @@ document.body.insertAdjacentHTML('afterbegin', ` + + + diff --git a/app/appearance/icons/material/icon.json b/app/appearance/icons/material/icon.json index 2aac5286759..0acf9352d9f 100644 --- a/app/appearance/icons/material/icon.json +++ b/app/appearance/icons/material/icon.json @@ -2,5 +2,5 @@ "name": "material", "author": "Vanessa", "url": "https://github.com/Vanessa219", - "version": "1.24.0" + "version": "1.25.0" } diff --git a/app/appearance/langs/en_US.json b/app/appearance/langs/en_US.json index c0a93e9df3d..a0c9c9d0152 100644 --- a/app/appearance/langs/en_US.json +++ b/app/appearance/langs/en_US.json @@ -96,7 +96,7 @@ "date": "Date", "select": "Select", "multiSelect": "Multi-select", - "commandEmpty": "No command yet, click to go to the market to download more plugins", + "commandEmpty": "No command yet, click to go to the marketplace to install plugins", "commandPanel": "Command Palette", "cloudRegionNorthAmerica": "LiuYun (North America data center)", "cloudRegionChina": "LianDi (Mainland China data center)", diff --git a/app/appearance/langs/es_ES.json b/app/appearance/langs/es_ES.json index b93ec63471c..cef6684e95a 100644 --- a/app/appearance/langs/es_ES.json +++ b/app/appearance/langs/es_ES.json @@ -96,7 +96,7 @@ "fecha": "Fecha", "seleccionar": "Seleccionar", "multiSelect": "Selección múltiple", - "commandEmpty": "Sin comando todavía, haga clic para ir al mercado para descargar más complementos", + "commandEmpty": "Aún no hay ningún comando, haga clic para ir al mercado e instalar complementos", "commandPanel": "Paleta de comandos", "cloudRegionNorthAmerica": "LiuYun (Centro de datos de América del Norte)", "cloudRegionChina": "LianDi (Centro de datos de China continental)", diff --git a/app/appearance/langs/fr_FR.json b/app/appearance/langs/fr_FR.json index 8dcbd5dbd0a..cb8776bfa91 100644 --- a/app/appearance/langs/fr_FR.json +++ b/app/appearance/langs/fr_FR.json @@ -96,7 +96,7 @@ "date": "Date", "select": "Sélectionner", "multiSelect": "Multi-sélection", - "commandEmpty": "Aucune commande pour le moment, cliquez pour aller sur le marché pour télécharger plus de plugins", + "commandEmpty": "Pas de commande pour l'instant, cliquez pour aller sur la Marketplace pour installer les plugins", "commandPanel": "Palette de commandes", "cloudRegionNorthAmerica": "LiuYun (Centre de données d'Amérique du Nord)", "cloudRegionChina": "LianDi (Centre de données de Chine continentale)", diff --git a/app/appearance/langs/zh_CHT.json b/app/appearance/langs/zh_CHT.json index e8d639ada5e..0373ee2eb4b 100644 --- a/app/appearance/langs/zh_CHT.json +++ b/app/appearance/langs/zh_CHT.json @@ -96,7 +96,7 @@ "date": "日期", "select": "單選", "multiSelect": "多選", - "commandEmpty": "暫無命令,點擊前往集市下載更多插件", + "commandEmpty": "暫無指令,點選前往市集安裝插件", "commandPanel": "命令面板", "cloudRegionNorthAmerica": "流雲(北美資料中心)", "cloudRegionChina": "鏈滴(中國大陸資料中心)", diff --git a/app/appearance/langs/zh_CN.json b/app/appearance/langs/zh_CN.json index c2fe0206bf1..ee5122631cc 100644 --- a/app/appearance/langs/zh_CN.json +++ b/app/appearance/langs/zh_CN.json @@ -96,7 +96,7 @@ "date": "日期", "select": "单选", "multiSelect": "多选", - "commandEmpty": "暂无命令,点击前往集市下载更多插件", + "commandEmpty": "暂无命令,点击前往集市安装插件", "commandPanel": "命令面板", "cloudRegionNorthAmerica": "流云(北美数据中心)", "cloudRegionChina": "链滴(中国大陆数据中心)", diff --git a/app/appx/AppxManifest.xml b/app/appx/AppxManifest.xml index a0eebcba74a..800bfd75318 100644 --- a/app/appx/AppxManifest.xml +++ b/app/appx/AppxManifest.xml @@ -8,7 +8,7 @@ SiYuan diff --git a/app/electron/main.js b/app/electron/main.js index 8c16bae7482..2bca368a0de 100644 --- a/app/electron/main.js +++ b/app/electron/main.js @@ -695,9 +695,10 @@ app.whenReady().then(() => { return; } const ids = decodeURIComponent(new URL(item.webContents.getURL()).hash.substring(1)).split("\u200b"); - if (ids.includes(data.options.rootID) || ids.includes(data.options.assetPath)) { + const options = JSON.parse(data.options); + if (ids.includes(options.rootID) || ids.includes(options.assetPath)) { item.focus(); - item.webContents.send("siyuan-open-file", data.options); + item.webContents.send("siyuan-open-file", options); hasMatch = true; return true; } diff --git a/app/guide/20210808180117-6v0mkxr/20200923234011-ieuun1p/20210808180303-xaduj2o/20221223221636-ms2b4w9.sy b/app/guide/20210808180117-6v0mkxr/20200923234011-ieuun1p/20210808180303-xaduj2o/20221223221636-ms2b4w9.sy index 4f16b562112..26a841453be 100644 --- a/app/guide/20210808180117-6v0mkxr/20200923234011-ieuun1p/20210808180303-xaduj2o/20221223221636-ms2b4w9.sy +++ b/app/guide/20210808180117-6v0mkxr/20200923234011-ieuun1p/20210808180303-xaduj2o/20221223221636-ms2b4w9.sy @@ -5,7 +5,7 @@ "Properties": { "id": "20221223221636-ms2b4w9", "title": "Flashcards", - "updated": "20230820185231" + "updated": "20231013104018" }, "Children": [ { @@ -87,7 +87,7 @@ "ListData": {}, "Properties": { "id": "20230219092249-yzjjb1o", - "updated": "20230219092854" + "updated": "20231013104018" }, "Children": [ { @@ -132,7 +132,7 @@ }, "Properties": { "id": "20230219092249-xdxmusm", - "updated": "20230219092407" + "updated": "20231013103949" }, "Children": [ { @@ -140,12 +140,12 @@ "Type": "NodeParagraph", "Properties": { "id": "20230219092249-04pxvxk", - "updated": "20230219092407" + "updated": "20231013103949" }, "Children": [ { "Type": "NodeText", - "Data": "If the super block is set as a flashcard, the first sub-block of the super block will be regarded as a question, and the rest of the sub-blocks will be regarded as an answer" + "Data": "If a super block is set as a flashcard, the first sub-block of the super block will be regarded as a question, and the rest of the sub-blocks will be regarded as an answer" } ] } @@ -178,6 +178,34 @@ ] } ] + }, + { + "ID": "20231013103926-u4hxd38", + "Type": "NodeListItem", + "ListData": { + "BulletChar": 42, + "Marker": "Kg==" + }, + "Properties": { + "id": "20231013103926-u4hxd38", + "updated": "20231013104018" + }, + "Children": [ + { + "ID": "20231013103926-o3y1n5a", + "Type": "NodeParagraph", + "Properties": { + "id": "20231013103926-o3y1n5a", + "updated": "20231013104018" + }, + "Children": [ + { + "Type": "NodeText", + "Data": "If a heading block is set as an flashcard, the heading block will be treated as a question and the blocks below it will be treated answers" + } + ] + } + ] } ] }, diff --git a/app/guide/20210808180117-6v0mkxr/20230405172236-pg3l9eu.sy b/app/guide/20210808180117-6v0mkxr/20230405172236-pg3l9eu.sy index cc748a3d5d7..94c08051296 100644 --- a/app/guide/20210808180117-6v0mkxr/20230405172236-pg3l9eu.sy +++ b/app/guide/20210808180117-6v0mkxr/20230405172236-pg3l9eu.sy @@ -6,7 +6,7 @@ "icon": "1f680", "id": "20230405172236-pg3l9eu", "title": "Performance Optimization", - "updated": "20230406155736" + "updated": "20231014100732" }, "Children": [ { @@ -393,7 +393,7 @@ "ListData": {}, "Properties": { "id": "20230405172238-ba6rydi", - "updated": "20230405172238" + "updated": "20231014100732" }, "Children": [ { @@ -489,7 +489,7 @@ }, "Properties": { "id": "20230405172238-2jjuvev", - "updated": "20230405172238" + "updated": "20231014100732" }, "Children": [ { @@ -497,12 +497,12 @@ "Type": "NodeParagraph", "Properties": { "id": "20230405172238-qnz5kn9", - "updated": "20230405172238" + "updated": "20231014100732" }, "Children": [ { "Type": "NodeText", - "Data": "Pictures and charts: Pictures and charts mainly affect the performance of the editor. If the document contains a large number of pictures and charts, the editor may freeze" + "Data": "Formulas, images and charts: Formulas, images and charts mainly affect the performance of the editor. If the document contains a large number of formulas, images and charts, the editor may freeze" } ] } diff --git a/app/guide/20210808180117-czj9bvb/20200812220555-lj3enxa/20210808180321-hbvl5c2/20221223215557-o6gfsoy.sy b/app/guide/20210808180117-czj9bvb/20200812220555-lj3enxa/20210808180321-hbvl5c2/20221223215557-o6gfsoy.sy index 7bc23d986d3..8e532a5e9d5 100644 --- a/app/guide/20210808180117-czj9bvb/20200812220555-lj3enxa/20210808180321-hbvl5c2/20221223215557-o6gfsoy.sy +++ b/app/guide/20210808180117-czj9bvb/20200812220555-lj3enxa/20210808180321-hbvl5c2/20221223215557-o6gfsoy.sy @@ -5,7 +5,7 @@ "Properties": { "id": "20221223215557-o6gfsoy", "title": "闪卡", - "updated": "20230820185147" + "updated": "20231013103850" }, "Children": [ { @@ -87,7 +87,7 @@ "ListData": {}, "Properties": { "id": "20230219085658-xnrf7rf", - "updated": "20230219092140" + "updated": "20231013103850" }, "Children": [ { @@ -112,11 +112,7 @@ "Children": [ { "Type": "NodeText", - "Data": "内容块" - }, - { - "Type": "NodeText", - "Data": "中的 " + "Data": "内容块中的 " }, { "Type": "NodeTextMark", @@ -186,6 +182,34 @@ ] } ] + }, + { + "ID": "20231013103816-b0k440b", + "Type": "NodeListItem", + "ListData": { + "BulletChar": 42, + "Marker": "Kg==" + }, + "Properties": { + "id": "20231013103816-b0k440b", + "updated": "20231013103850" + }, + "Children": [ + { + "ID": "20231013103816-sx34w1r", + "Type": "NodeParagraph", + "Properties": { + "id": "20231013103816-sx34w1r", + "updated": "20231013103850" + }, + "Children": [ + { + "Type": "NodeText", + "Data": "如果将标题块设置为上卡,则该标题块会被视作问题,其下方块会被视作答案" + } + ] + } + ] } ] }, diff --git a/app/guide/20210808180117-czj9bvb/20230405155631-leo4vc6.sy b/app/guide/20210808180117-czj9bvb/20230405155631-leo4vc6.sy index 2147b364231..cd37393627b 100644 --- a/app/guide/20210808180117-czj9bvb/20230405155631-leo4vc6.sy +++ b/app/guide/20210808180117-czj9bvb/20230405155631-leo4vc6.sy @@ -6,7 +6,7 @@ "icon": "1f680", "id": "20230405155631-leo4vc6", "title": "性能优化", - "updated": "20230406155719" + "updated": "20231014100210" }, "Children": [ { @@ -389,7 +389,7 @@ "ListData": {}, "Properties": { "id": "20230405171343-t3v7zis", - "updated": "20230405165229" + "updated": "20231014100210" }, "Children": [ { @@ -400,7 +400,8 @@ "Marker": "Kg==" }, "Properties": { - "id": "20230405165213-7f2mie7" + "id": "20230405165213-7f2mie7", + "updated": "20231014100152" }, "Children": [ { @@ -408,12 +409,12 @@ "Type": "NodeParagraph", "Properties": { "id": "20230405165213-tb7ozhd", - "updated": "20230405165216" + "updated": "20231014100152" }, "Children": [ { "Type": "NodeText", - "Data": "叶子块大小:单个叶子块大小主要影响编辑器性能,如果一个叶子块包含了大量字符,那么编辑器可能会卡顿" + "Data": "叶子块大小:单个叶子块大小会影响编辑器性能,如果一个叶子块包含了大量字符,那么编辑器可能会卡顿" } ] } @@ -427,7 +428,8 @@ "Marker": "Kg==" }, "Properties": { - "id": "20230405170653-z2vsmgf" + "id": "20230405170653-z2vsmgf", + "updated": "20231014100157" }, "Children": [ { @@ -435,12 +437,12 @@ "Type": "NodeParagraph", "Properties": { "id": "20230405170653-xsogb1v", - "updated": "20230405171038" + "updated": "20231014100157" }, "Children": [ { "Type": "NodeText", - "Data": "动态加载块数:加载块数主要影响编辑器性能,特别是对于公式块、代码块和图表块较多的情况,如果加载较多块,那么编辑器可能会卡顿" + "Data": "动态加载块数:加载块数会影响编辑器性能,特别是对于公式块、代码块和图表块较多的情况,如果加载较多块,那么编辑器可能会卡顿" } ] } @@ -454,7 +456,8 @@ "Marker": "Kg==" }, "Properties": { - "id": "20230405165219-ssntm64" + "id": "20230405165219-ssntm64", + "updated": "20231014100159" }, "Children": [ { @@ -462,12 +465,12 @@ "Type": "NodeParagraph", "Properties": { "id": "20230405165219-tu1gvvv", - "updated": "20230405165223" + "updated": "20231014100159" }, "Children": [ { "Type": "NodeText", - "Data": "代码块行号:代码块行号主要影响编辑器性能,如果一个代码块内容较多且开启了行号显示,那么编辑器可能会卡顿" + "Data": "代码块行号:代码块行号会影响编辑器性能,如果一个代码块内容较多且开启了行号显示,那么编辑器可能会卡顿" } ] } @@ -482,7 +485,7 @@ }, "Properties": { "id": "20230405165226-j1zfdmb", - "updated": "20230405165229" + "updated": "20231014100210" }, "Children": [ { @@ -490,12 +493,12 @@ "Type": "NodeParagraph", "Properties": { "id": "20230405165226-tc75whx", - "updated": "20230405165229" + "updated": "20231014100210" }, "Children": [ { "Type": "NodeText", - "Data": "图片和图表:图片和图表主要影响编辑器性能,如果文档中包含了大量图片和图表,那么编辑器可能会卡顿" + "Data": "公式、图片和图表:公式、图片和图表会影响编辑器性能,如果文档中包含了大量公式、图片和图表,那么编辑器可能会卡顿" } ] } diff --git a/app/guide/20211226090932-5lcq56f/20211226115423-d5z1joq/20211226121203-rjjngpz/20221223221501-mops33i.sy b/app/guide/20211226090932-5lcq56f/20211226115423-d5z1joq/20211226121203-rjjngpz/20221223221501-mops33i.sy index b289fb8f5e2..f0e504181e8 100644 --- a/app/guide/20211226090932-5lcq56f/20211226115423-d5z1joq/20211226121203-rjjngpz/20221223221501-mops33i.sy +++ b/app/guide/20211226090932-5lcq56f/20211226115423-d5z1joq/20211226121203-rjjngpz/20221223221501-mops33i.sy @@ -5,7 +5,7 @@ "Properties": { "id": "20221223221501-mops33i", "title": "閃卡", - "updated": "20230820185212" + "updated": "20231013104052" }, "Children": [ { @@ -87,7 +87,7 @@ "ListData": {}, "Properties": { "id": "20230219092911-8074ovh", - "updated": "20230219092911" + "updated": "20231013104052" }, "Children": [ { @@ -182,6 +182,34 @@ ] } ] + }, + { + "ID": "20231013104042-7impulf", + "Type": "NodeListItem", + "ListData": { + "BulletChar": 42, + "Marker": "Kg==" + }, + "Properties": { + "id": "20231013104042-7impulf", + "updated": "20231013104052" + }, + "Children": [ + { + "ID": "20231013104042-wzwhxmr", + "Type": "NodeParagraph", + "Properties": { + "id": "20231013104042-wzwhxmr", + "updated": "20231013104052" + }, + "Children": [ + { + "Type": "NodeText", + "Data": "如果將標題塊設為上卡,則該標題塊會被視為問題,其下方塊會被視為答案" + } + ] + } + ] } ] }, diff --git a/app/guide/20211226090932-5lcq56f/20230405172131-yb16aax.sy b/app/guide/20211226090932-5lcq56f/20230405172131-yb16aax.sy index 46198053c58..9b0b55ab7af 100644 --- a/app/guide/20211226090932-5lcq56f/20230405172131-yb16aax.sy +++ b/app/guide/20211226090932-5lcq56f/20230405172131-yb16aax.sy @@ -6,7 +6,7 @@ "icon": "1f680", "id": "20230405172131-yb16aax", "title": "性能最佳化", - "updated": "20230630150049" + "updated": "20231014100319" }, "Children": [ { @@ -393,7 +393,7 @@ "ListData": {}, "Properties": { "id": "20230405172204-rwrw4h5", - "updated": "20230405172204" + "updated": "20231014100319" }, "Children": [ { @@ -405,7 +405,7 @@ }, "Properties": { "id": "20230405172204-73vvmgi", - "updated": "20230405172204" + "updated": "20231014100314" }, "Children": [ { @@ -413,12 +413,12 @@ "Type": "NodeParagraph", "Properties": { "id": "20230405172204-cawwvgz", - "updated": "20230405172204" + "updated": "20231014100314" }, "Children": [ { "Type": "NodeText", - "Data": "葉子塊大小:單個葉子塊大小主要影響編輯器性能,如果一個葉子塊包含了大量字符,那麼編輯器可能會卡頓" + "Data": "葉子塊大小:單個葉子塊大小會影響編輯器性能,如果一個葉子塊包含了大量字符,那麼編輯器可能會卡頓" } ] } @@ -433,7 +433,7 @@ }, "Properties": { "id": "20230405172204-yl6jz5a", - "updated": "20230405172204" + "updated": "20231014100316" }, "Children": [ { @@ -441,12 +441,12 @@ "Type": "NodeParagraph", "Properties": { "id": "20230405172204-3q6o7u5", - "updated": "20230405172204" + "updated": "20231014100316" }, "Children": [ { "Type": "NodeText", - "Data": "動態加載塊數:加載塊數主要影響編輯器性能,特別是對於公式塊、代碼塊和圖表塊較多的情況,如果加載較多塊,那麼編輯器可能會卡頓" + "Data": "動態加載塊數:加載塊數會影響編輯器性能,特別是對於公式塊、代碼塊和圖表塊較多的情況,如果加載較多塊,那麼編輯器可能會卡頓" } ] } @@ -461,7 +461,7 @@ }, "Properties": { "id": "20230405172204-y2kynd5", - "updated": "20230405172204" + "updated": "20231014100318" }, "Children": [ { @@ -469,12 +469,12 @@ "Type": "NodeParagraph", "Properties": { "id": "20230405172204-92zmfvs", - "updated": "20230405172204" + "updated": "20231014100318" }, "Children": [ { "Type": "NodeText", - "Data": "代碼塊行號:代碼塊行號主要影響編輯器性能,如果一個代碼塊內容較多且開啟了行號顯示,那麼編輯器可能會卡頓" + "Data": "代碼塊行號:代碼塊行號會影響編輯器性能,如果一個代碼塊內容較多且開啟了行號顯示,那麼編輯器可能會卡頓" } ] } @@ -489,7 +489,7 @@ }, "Properties": { "id": "20230405172204-3o54nkp", - "updated": "20230405172204" + "updated": "20231014100319" }, "Children": [ { @@ -497,12 +497,12 @@ "Type": "NodeParagraph", "Properties": { "id": "20230405172204-klp5sm8", - "updated": "20230405172204" + "updated": "20231014100319" }, "Children": [ { "Type": "NodeText", - "Data": "圖片和圖表:圖片和圖表主要影響編輯器性能,如果文檔中包含了大量圖片和圖表,那麼編輯器可能會卡頓" + "Data": "公式、圖片和圖表:公式、圖片和圖表會影響編輯器性能,如果文檔中包含了大量公式、圖片和圖表,那麼編輯器可能會卡頓" } ] } diff --git a/app/src/assets/icon-mac.png b/app/src/assets/icon-mac.png new file mode 100644 index 0000000000000000000000000000000000000000..690dabaf4312a094c7c3052e124bb651ddc3131b GIT binary patch literal 28687 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

CT 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�|r#OOx5ukI9`Lir^o`X57l) zYPLbXBgXK|RCLc3;QlQb$89x*^R;ykz7$N;W7}+W49^k^oV73Oqd5%S`qU(aZ69P&+tFb z{}$il;J2x=I%h?nu2ktO2`_xa4-cwR23`FibATz)Hp%~~Bs-El>J)-L)qQB`DUL7- zdu$p6m{b9QS09Ab?ptu3=6tKG&i%SyyFcGoBGQJ+!(?z*IO6V)qWTZc%sJXrA8WAg z+ck#^n}Hn2jJ!MZMDdX8#@};a+h_4W{fQr<)!zb<%Ypm^J)|PkNP;aD*7-%XZuy$# z-Hw;$5cME<-0@o*OmmlpzDMcNc{A*%V4C96&$x5m9FBCA#p2_- zHq0ukzLSCC!418Y1a_eLUEb8+@=*#Wk=OF1j% zQuOFU#yqq5X#Qco@e4&gD(qk~RM{rfRsDm#Eak|pq2+?2$d@ll5EuPRbk@&rlY(q| z8oAU7KmGlglx>5VBbg<*qTjArUniCxJL1|?d>9XOk!*k;;qo0Z!iiyRK+ZdwYkSF~ z?~TTJ&k^3^6M`agUk|%bBp$p&>6D3tn44o?{4`UcciclNcCe_$d=Ju!WLCKF3}sgN z8E_^#K{7ktXQxtR!_;jf)tUTWWqjc%dvy4AV6S|E0}S-zZ7?%o<_Y`!seYUDzwnNa zZKyz{+)w*$PBB7lIPG2LY0z<*d2xfQm`do@fj6app6thM4WNf5OO?4ikD^TE+;>{J zEXcQBWt@nPx`t(g>Vbh`t;Rf8fw>bw|s<#+YQ#*}5Nxc4 zG~PDD?!LT}9C~1mDAL?(o|APSCsHEY0izn)zij4e#)YJQ@Z{^l4$XdjHgSs+maFHb zmz%-hcCgJa=(ASMT3d`@pF1$ zrV{4c8u3~^k6-*IQ$C0cUF~SSv)v~)B#-iia2eKun7KcKjhIZ^6x&Cb@?N&M!LL-c zJpB>S$baXqL60Qx$jv7-3xRpr)kJcAoJwbwIYdaso`;inb`5M?O_7ZDr7djY2m7xV zVlBvA0{FW>xPq*lJKiq-D5~?kI00s|{Wk+{x#qWc#JHW(ap5$vQqR4Tjn(_RS1_GqgfBzIyUuO?iW-cRF~brj+dA zXwlPpe))mjEBu|*rS;l?*>Rh4NcD5V-G%@&Xz0_Ez@9r}nVp;w4{9;(1MX0Ca#)$(s^F+ zc^_h79lXF%=3^lz8T&xm_NRpT*^$fjHNdWk1#^8;N;u{2VT@bR=eydgwPnnOocj>> zf=~U2ihXc>&O;Gtmdbb5uhJ@8+;v|;cxjPR`UfYjO8<7i2L2v?w`%KOp>eB#0&bYU zJ}pyt44tlpX~gQSvcp3z;9SO9nnN^dijP1C*+l`TQezkq9c^%#XV>3C5zHxLY}|ID z_qbyvX>Mw{`(^mnPFaU8v!#3+#*u$->-w1s`lk8ZI*~Ueh)^egP{U-}`GPVi(2KuS zGu}|z3YDTL$zQquy5&Kxm@h=UZ5WSrd==J`=j@VlKCP?JO2bp#J>7+nDQ(N-Q>J`T z{JOYKds4?>zZ9D8q)pkb?}+y~ET2dg5?HJEb7!3ZU#KheegcO8Et=)#_X<3JO*#S* zmS(@D;{(^yfmToMk*Ee~xG|{sdGppZqOMc!3Y&O0`4x28?c!_ch(gFD&9ZLS$Hc1S zInt`ee3JU&LMpFxafiH=h_v-&w>;@?BQ@)jU@Q=f*WiS%M~=G~Z^_;7)s*O~6rg3k zEG1`qjQem>U)fkI90b1B-t2&AJO3_0?_(v$O|l?{tmTDfWym6@+h8&c=y&>Z!Aj@z zj@f~0J^F_72kEA9vM4=pRdVLkv37a&gQN{@;nb6pUqlftlPZ^j7n|N?zNLbhqk5fg zPo=fIE^RcO3VVISz>DNOhWl0JZUD1R6_d8UYOPE<@i~iiBVPr1l&QTWoI|K?3NWD} z$Bw)zH1$S9@o=DR`tB{xwDG1gwa+bh4r5nJQ@TYTZo$p zcLS3VrS3fT5DN&Qs|L&#jxi4^>ZACm4zK2bE-V2S15YHHD_!~u>NS|lFtwmZwfRUU z{L#sg#(OTpATq^yhx;?hMU@pszho5hU)fv;VC=^f{T$v zakInCc`HHwq?R2jJm=HfHv26Z-gPf%(UxUT>zYi2!F3JKZD2&W!H%7Il?`Zx`_Pg=OpV5}kEE9`KR%hHr38{_LttB)1=U(L z`$Z+#tyU!k_^h=G*S-`QPpE&&&CsqGPd9JEUxfjUA6BaNsCUuDue>!9Bv-&ibL&^% zu6;Yl75G|64VZN3`(vd`X7VfUd^hdG?1KLmlj7(7jzOv!k zUK-PlD;L6mPP@8W?Q?+f+P+eF^W9PCeWw2S`yarqkFD52cOK51Im6Qf=;fObe;d#bZ_(cfP*Tye5@3x(D+>i?y z<|;b11hgi+9($@J+;CT&+bUTS@E(-L2`(w=gcP6V?UNp!B(qqiqm4+nel6W{=$C_o zR@wDSA_2S#Bav9P>%T9KkG>Vyp&Nr2j4xaxzAkipKme>)|GCaV+tf}+NBHNNY&Z-K ztv|!7>vreI4NcDe2nE2<&#FaSe#BW1b$F;b5tF1zx`B}Z3Ac{t>#Oo$ideL=Xj4Sz-=E2OpdO7^H!P= z@L$o;8lK9$CbFs1!3{;?CHz4(`42axt8G9J{qD^UJl@O5D?;o(&W<8k zlcYqQ*p&;XK>}&S^o|q&;LHPhyLEXZ~8Q z3`Cm85%P>@$$6@yXG%=HqEB9`#Z~mti?#83ztedM+;=DzEo5N~U-Rx{$o@C>&>|{= zVAg1miOoGnjqQsS&D`79_24sb>WGGUFFw8CeQNsL^)~i5t@N8#A)Cn(r_nXJ65fw? z$ShZNhJe7KsOX|E=;5ZD!Qk;}j1-hz^ua_;#BME_62fGPe}vM9zFqY@@#Ux0;agH} zyd@~L)HGDylJG?D>FiXWB4HIdNdZ)W^g%JP<9ZL=cPNTGQ=IHb`_I0#7e5i1vE*&= zD^*@F5(rk{vbZlYc(mX*j`|yw?2(DTV5Blt)Kgwn%I64CmX|15ddRJ~^$#x=Tm|CD z?*>}sZGq;4$5QINn_?R3?wn1mB&>rU#qJ=;KEbo{KxL-X2P< z0%p%AM>xH&N?HywJy*-7YJ|8lPe-PZcWNDeu6KL9prb5f4=F=(2ZDe>vwbd3hi_ld2>yF@MpKBThe) zszEs2KmrOqa41U}sFA6(nXK3=0m=vMHE`WYezomm0v+D??NOG+EM=Rubj~~UDT~c; zUy+_VVV7@d(~-SOKSo~=^507>#KLj>K3xzZ)Fg1hYGuCn{GoePKsccpriBNFzO6wx zHtU=(R+Y_zJcNi;!Q=dhkHbN#l-uMBKitS1a3~JTAUREx@8!oFyMM za=x1#^_t|4`l^Zwtp9(K8jSg>k;>AlF#ydj#8w!Cp zgafDgoJImJ-MfF;hWKZC@0q$v!ONEs6TNAg_L-i!dxBL+C70(|%5tny#~C31pY({i z{G+sjiW3XTfUcEXU(@_E1%;);6Je1B9?SmayVHeZl!k?xlrP%{lA+>h!J4APvw>_~%Y`)Yk&*=W$udRlu}pZu*m08C*BpwU^r9 zjm3r9zh&)wSZHi?!locf539y5Oa^2QR6vUWb~?1=Zp3TCJq7OlaxU&bgmOub1EAF< zD$p|<1%VTaptnap+SL};*FP{+_BfaIw}|~nJit4^b`=0|nWAqPq zJAm0b@uxCwXx(dytNDM4F~ovK%B(9QU(S266)1qIExYhZsa1B+uPj_`0LMI(I1my2 z-u>?a5YIX7?X2We7tN5<{+j}nn{3FDWCY*{Y6J2es_vfC=>PrP1Q3ND9wp{;2*ght z!OiNRzp^r;2sW??LU+UTA|SrRBq#RJQ}Lga2yiM3u9A4Yc@BXG%l0nJO#`=qvr2Co zYB@=BvJNJfGkb+YJiXS4^k1ut8Cx6JXLBulx3TuB_%~yZJK3^QBZY~L5GTSFB5Qh; zAHLr$8cKU6F>L#Chx6q`Hh3mHJ%5oCU|WafSs75g?E0`WjKbD&YXviAH-x|{rp!i zgXKjVpq?~v)pZ^uf(CI0Pm*)Mk^-P(>V3V6^4rukBt0P0yVvuFqiMs<{~k%o89GSE z5(&j_9VL5!aUiyZp+_f6QPQXfnKZG)dk0u*#~OzTNK zjyl(BQ(&o_E{xp*^l*|-uZ{tKh=J9qN}7NB46e$fY+Uu5mr5J8vJ>xrx20DIlbu(A zAn=CVjpV!c?AOjii`|zC{#Or2407PWfwD5&n)fXDYrl+KeMdq7J0s}Ld$z&kWBZXI0B;0;M>#CP5rX<4$43zu?S>6S3Z4oxi9(|xv_PxQFxB?lI}m5yIfw} zBaK8V@?iJT**=Py&8-8*h-m$uwbzig`xQ@htU>CUaQ^Cs8r`=?{;fC(s*ddSqf-Xx z6jKZ>l(|i5YTy{T2vijs0K@!}-VPcKGgP=dJpl6wY z3n)%e-w$T!&R^NE1b(^Vy9robR%%;~zM-}RH2CZ97O`0J{O#DG@i{U1&dvAbXm2`N z6D?wc6)3R8tI44z%EIY6M#|^PeJ-EJ#K>=%bVpjiD3RM2}~YUM;|cDRG-2S zz&Ia$G4g1%mj#;%FCji{KnS$N;Fu_G*f>)JznP9vb_>Be(CYOPJ%TR+T#SPuo`D)fYJy8e}qTFBc! zlX+*>Kk=`fr6Acu6v6f#D#$?jkgQNNsJz4T1W0M5TBp>)__?wT(7Qh}8vOL{HxJ)n za~h|Q_2ZG@B)?9V>hU4LtECqWolfZlF>dpw~F58?-vE5+@Njc6khe zPksFF10ZZoKuF**pu1_3%=x;2yOwqZMnG>5{Mr5mflGpl%GE$h=?OOAa7zn|O}o4| ze>X#_I7FRC=zEe|JU%lqYz-OBm7k=lzH9m{+lTw7blg&M)O#E>|(5=Bn0na&5rYUso|$ z+Kw@zmAXS6|946V`Jnlj2cqFlLITN^ZO=bd(`nNPhtFl?i0!aV3hHRNCBc8_fat-#)Lk$b(%EE%u{+MkTp6ZFs=UY|}%;bkl3E&PUney8NK z4bXj(G{kP{qKzJy%xqkiHDX#_G z$nN7vRZmbhATe@bc|%{e3(}Td9Y6QG0n&ST(8R!d@p~bXvTvQL21!jf08LMzFHfon z%0pbqI^M6<`I(aQa}9dDA)?2HMr9e%+2Kl@brQbB3yYRrSp zdFn3ONnd=?^?(Rf*-58XO!Xj9@8!SW2tXjgqnG^_`&uN>-&5PMZSrVjQC5>=eRdX%G0^jw8%c-3IhXl)|~|pbjHY zOsmRu7Kp6V?3X9$t>m9&syVt3He|>CN6FR=0w}twDce-e=HK{e+XdGbSP&qiXE~4_ z)Z-bCg=x`FXHzgnjlr3G>TKqBVCu|mBN2Q+!)w;r5)=o%H_K19f9mAFGw&(|LeQ}< zfF-3f9jLI??YE_k{L$RhZ$#WByJPhSDwuPOu7LuPtcfsYohW*AI*1*p&sIYLb-X#L z1TC<8_@c360~cml=bw<@(Zk@xO(doIhw0u>KAnPB2~ON5+4bam_)#ock(%2D3xTH#@Lcg>Pjx})zk7R{|4Y;QXR#g)XUvtd=ADDgVJ!sB zc7zJ@_+HSqids0Ai@%`)=#16#W-WWWN-srF1Ydb5X(LEUQIVz^+Jh%{$+)~1%qX1zVGz%{i zaxc9swD2L3Fcmij`|l6}Zf!2y!r});pFEU8!?a)bhYnRS|Ey>nch97-ZcX z!U&bLY1O0OHr$AO1KLWqpxMZ6UEwXS$u+!CaH}u-q+~OpgQHb@KGP-kx|Nk=eAw{K ze+H)EFzW3GHEi%q68+(7Jzg*7D3a3hJ!^ox_YP!Ez;n>cJ0-b-?z~v9y>Kw!dMcgjs!2;xaZc~Sr~lj;%oBn4o?BuQ8T}|cgUlezHtstIv#*>Uk*HC0Bo7Ow?#mb=Om%}9Okw3LH+Aj#2&pt9?!ROyUq<0BB zQVzHYieIy|JPGbD3}w#z86*j*pRBmaFJBRwQHNQJJFIxZd z_TkHH+u(z$8r}k{5vcCp5SFZSPA#&i?x>|zK$uzv@LYNmVu2}C!T&Ot=cOv3>P?6r zl5}GCN#hJvhm-&&xb+91`hwo*+mgI#=daI>^oVw@YM$!yDm=uNDYZ0pLSi)fE_po%@ z@5a@JB^QAgSaaKJeX|2XEj2q{UUKyEDo1N@o2*f@tkc<&TKrN|GBindniaypYinZ1 z*PrE6W@ECmv+2=%Z6c7p!-1fW%>UgrIKha9h6a7--;-kd$50TkK*MqZ$p_@K*Zal? zp0_;M^*cSj!Oel(i~W7DSW(bOO1X)>`364d2>dykP&j|q7(RB;$$`lp6TT$GmKL{E z)*3#3a@2lv8*l-LJ!~rWNt^qrWuPA&YCG6Q>O6O8UpJQ)t={7KH3wI04g{fqP$@+> z4il)1olQA+kHmi#`BVT!8^lm;XTH`bfJbDVAlNjJnBE$7Wk_rCxakaUmTIvGk??{2*SZIy4?n^{`|w5cIybgbyr=+>Ju3!lloH^P-@Kzka<@4g*Np5zwq~A0$@(D67pUveNWr) z(1Tek!(?%7<_V^3FCL%$w|aHi2`D72Q2{C82SUkVt+W(6F=ZEX|E@|nKTTj6h?uQ= zu(lNYO?!Tt+?H3;IU}m9Q^)((-J$J9esy%{`W0Svzg8Qj4tA7R&MT*QP}!X1Sy^#6 zt}IQ&q(+?h{V%?N^~7uabzUOC@`K;MSArMk_C;ldeeTgVfmY2=gUve=GrLO4#MAsV z^nfyxb35WJc7*BXO~+$SXTN1^oc}2PIm2VmrQj^j4QK}q{=XpN6(T;=}>ZmDMaym!x%GRF|g{*2?oQ5-bRYX7hYpd$#Wzaqqie zjuCB07bpzcgeAhB^c9amhy4S4@K9ayNo9m(JvtA|_-*-zYPqYPH`blhHZ1uoUA zGp;h2C7oIZ&3xMC_l_r1+G%%6lG~yS$Z2S?UuBWsSFD@{R8rlRYesK&0_Ld+xmr&9 zPriDR7scl^p;p{D-Fjgp8~i}}(SDANp=h7@2Mk}`lk^`nuzaMc z&KVAkTLub8`~@PMhfdrXH2)+i5#}C1tN??Ja;bkQXgt9oirF+i!P>J)LNm= zO$1@7;^(%r_f=fY@=r}!x zzLo{cL>=99*{Ka+@gLTa1tC*k?IH8J1!k%yDMkSt4IjpNn=(` z_wtxenM&EWobkp5%O_g*2SBv@zJw5>A2&0`k?Cs&Pk~7FnpGW6*2N1KI zBP1t3-!+<(b`rc{YfYp%PsC%J0%k5jq_ zs3w8UUX4J$|AaskFo^Q`KtyGmLNcp?fe6I7!-*)tSttu`VHGsz6ZdCkEd-MdzO8H4 zR;XQ=i11Jp7#r~NyrG_-jGt!e4x@bir0b*4uNk$14xrVy`%+~fg-1XU9<|(1kx485 z+rQ8*J2`9e7Ui*RMdkfI2O@I4_Htu*g2v|8aJsblFsZ@%<+0@G=X+e4i$Nz&oLG)# zrI}Xaf`G47{`6toah^XrwLPJ}m}6hOXoq9ylj~IugO~vHDFca{am?DaR|ey1J0ba) zro3(ywLs)D`;PC&6Da6?$M4`?)S+gv2%p?BNl)uwKTI0P4%i7bE44Ka16Z^~ORZPj zXm2JJ?NypphvlQc&FWYpNb01-4B*%DuD!(X`s+&wM;zrTZ+$X3FB{rIRf(ma^2Cfx zZF)6Uo!tF(_Hf?{A^R*Qy45SE0I$;|Aw-_?>)fq&pZdPD2CYqJ$wW14F}*l&riC9e z)i4zpta13z4VDg!9MQ|GyTikKGjFQKD=yy^(|aoz6Yl5rxLYNr*2^(?;>S9n{RV(>R<-OyC*}eG3r-WL7H*Hd-*E%3P8fzzhgdg`ndo2<4yzQpfeZJ8r zFfLP@I+)efmfYO3Fllo|2$k1o!XXE#Ou?Y*`?2USk_Z3DI07?ulKD(ZRvTW!8=vpe zx={UhU}KW?4PhU9*YZia2H zjx_B*cgUf&E-yJdeE1^v68|9UiSIvuxXgEKyJPI}YR-rAT65_+qp`a&Yh6^6sj&Jp z^&ZRC!Cu`Du7v;blpI*Rhoo%-g(Y8^!E{tF7W)&>+q;PgY;l^W^$VRI7}4!c1AE8q zVoquM<$PVu+LL{W5WiJB5_c-la>OgPbNepF9@CNF)%YR6niv!qPSXS4CaAX0#UwP` z>RqFYgTrg8G!H98AkU$&KRISM0Nodc#-ckt^m?L$iA|KSog92z8MkAn%X80G>WEG( zbn9i3bD?1B9EDtioeBufshwXVv=9Q_yXyz8(dx$$d&}Z>K{eRqT{nlskga)b9Yg3t zX>-nqv0rC8_8$I5&)CP+X6;hKCM0S%>)k5-{93olG@)DI?IJ}E#Y^GMaYzjMiF#`+8 zzydO`fD9}k0}HsHOkiLE_nYGwSitiPEZ~1?Y78tO0}IH&0y40G3@ji63&_9%GO&OQ zEFc35$iM0U2081{RQk1!Q0W8CXCD7Lb7jWMBaqSU?6AkbwncU;!CeKn50&fdyn> z0U2081{RQk1!Q0W8CXCD7Lb7jWMBaqSiryH)eJ146oi2VWMBaqSU?6AkbwncU;!Ce wKn50&fdyn>0U2081{RQk1!Q0W8CXCD7Lb7jWMBdRFM#^}pTENYCl>Jk0dH<j73xNCCP zQAP#;;IdJs#=7*hr)dEIpx4*aGK2o}9Q`7wpub*$fm;ATv97Om!NNbgE7jG+Lic1V z)@j9_^*#rOYi;V>qyHxt4A~l5&OC!Lc8^;;Q3v%u*^IWk+>AS z;hdF3 zcgZa|zxoFmIgI`rgxxbAn^Fx%v8$@s`b3YV(TKO6bN9{`HIYvzEP6KIv4sf{1lP8O zto~TK#6J%VbZwux@_N&1zhb$~=MC>?+vlCcM}N#^Zdb0Fvn+a!ywVV?p*?xo>*V{U z^x{oBX7_Iy**2^tX7!tHj7eic&Wro^hO^DyPh{12{nE0dNuSR9G5^D=>^h~a7i$Dh z5ypW>As2J|+}CWZXkvfNSUphNAg@+(ilgK_mR7}!tw99C+;)p5PV{;%)erBPDc`Z`WnTv-aZj%l?3y?VZg{Lz%J#t?(B87uNWVQ-aDv*At!lxo6Xost zNwN3!lTzOMFdrU<;$38{RbFRCX0m73-CpqXJ85#P1^$3p%;MyzZZ06%6{Bzbsw}fk zCX5+8<$zjuX|1=s7+LdZ&nhgy(sfsNkuI95#j960bEYdMRC43v@3Un?pPO%TU5Tt& z@sgKgM^IXQ`rB}GyzCzkl%Hz;_x^uwPXn$&2A`@KAm%T0BdtA|ISIbF!wI_W>O7B4ut|P13=69IVzl+ z-0KmHpXhK%m8E+_t;L7?B1K?g6Mc}u*T;8f!!K}LN>@5h+B{6rfLm~7g2HV@SZh(w zGc0<}?2xx2#bs;ZFRUX}fe$aCzWVT8_KxLtiygA6KAMj6GFRD&1lm6u3BP8SUT%$C znTT(3iSu9flmj@Hf)(X@0{0tB#zs?~UNVAra*uQj<{Ejw2RqFL&ymjKZ`qC9LCA1g zy!dfuDkyJK#4p21*Jmes!R-C!iyKomznIX5=$!)f&_KUAm&#!~68TXj$o?%`p(s)5 z)86up{qX|HD7~~-*_oNIo&{gpI18}vTpN596ZG_>wvS1b{SC6f>GQ2;vNl3f^78edg8S#gd1NMK9M}?~0X$`kql~x*tREzQQUvq!!Vh z{eJ2OrJjF=4U*)%c>5NbBv=t?73hbf`1zxb&&qXpGNF(uzKZ3$Abi5Q@tdW_!`97# zAMFAf&7E7+UOChrur1`I19%ZI{h{OI%9wX)l4)2dFM@)|T(S45JmR)%wYn#{K3jzE$nUIgDO&b_R z#{^EhQYZs51T>+SZU(I<^yy)mmtEN8>7PiA2eTb6XBaOP=Sh4$HJt8GDd;q#WUr?uz(W-(=1lLygdcqC{^Hl0 z2Y{LZ`V&>foPod1YyE2s;MBwqW1sG_00DlMN;XyJIGN6LipTJ``o$Dj9@^q~m82(n zc=Vc>#M(I;-N714CiGgtWI_vGSD>^@?D}Px%-RSOUUz;e!9ZHj*}uGHse*!A=(-&X zjSuBO7`HxxTPzIb&8oGGG}E&6ufj@P=Z|^#bM6drE?-7~a}o5fD1O^HQ>F6Ww$Yfn^aB47VBP9}dX~#PX+90yk+Uo7 zS_1b>?};n7(Un|S9MBr-^eV}>@`|{@v@bcpiRsLCU6YL?6?P9ZsG%{3U*KH_+d}-j zJzv+~=rV}U&o1-R!?YDly-$PDZh3$HcOYgG1bg|4%?Hj&udt{Cj-c z09Z<%m)#Nf5~u*^$&@&S4*b?`e^Wn}cjkQ{I4pp0+z z6qrF_KCgnNZj?XeQCNM8S%L2l?=i99J5Pgm_T!1pb;#iAoj{vsavvU+g7QQq`ikAj znV2TinjWQu2&Nr@WT>^d*R8m{dF?!rZLY#p56Ogw;-C>>Ot45f3jd z)~tCEmTRwA)O1#0shf|t3|G76tU{kyCSC@Pp~LMM9K-xFF`*DdjB6@xD9OUU5hgEyn*n zk^+mQx4=z_pw;Q?3w z+-#Wf*R0lV=Px5`d`MpPJmHg08{yTR4%2FF`mU$UKVSRW=bFWQXc~S^bfWlR*(PmG zc@^uUM%>TY7v22v^@OprAbghIWW0#9ya zSX4;VoQSJ~e^y`~GBJAiAdR^`KGoxXuu(y1)ox(P$;0~IxAFD-SBdk{Lb7qL>n}Kj zmH)M@a{^&x$hjnwi!b zxV9DkM7}#F=^X3jN4RRuSIGlzW((HfyI<|b=XL=nYJ&{-RVS{6t}`G1dfcuN2I$RhjZpFP%=52NM`CtV zo;;PUlkdGVcL_nm-c6gre#7lLmSeQjNKrt;`-!v3{(EGVom|5ZjJW=BY|3SmDP%h! zjp}p{(IS#$>Av1=nkowJn&_OyfALkZ}n!7$=AzW)S{_N{}FpmoW-?IQI zcz1~_%;Ssi9DnJFC(>Sa^2CKgQZkdef#3p_caXOy-ZU|^W-vVIC+D{g~>uVmxrdiRxs=o z?B&s;W#&_!d}2!W291!wovXNq7rJ5oRa$#m7U5@;mnaC3$0_SKY

}_624`ky4$M=j^a~7lr1yNDiKG@>a#Fix8*U5N6UKf@+=wDDEhS3u5J9vxj;=Ap zG&DuvZhwY;I3+Wak%Y#H4j~4KdU4Qe*O#kpbvHLvmmrM}fq55qFn8*O%4jFYv--JJdhT+CAV`l!=4i^2LUC z`o+uBXnj`^7RB{qb6{&$k`p4onN*n<#L`}X-tTNGiW^Sg?4iQi+9c-M{K`M=>K%>X z%5!`7tl;o#x!WD%#Ypvr&@+*oi`|M*e|4_aZMc6d;g z*p5_lpoc+|Wp8Fz`3at{Gr~hT*}>kSd9IyHZ-Hq5se%{8SyAEh*CshB%b8wvU7Sqp z7^ObvGxug0bGJqmciLJP$i>3$)8KPic^p#l(`}b4%P$n=h7jSuZ>8+nJxSwV`>19y zqEB=6B&L-5C6(U=%~EQ-24E%gQLrM0sh4+CbVQrxD*dipr_XzO4{LA;u4ti#%c5vo zDgEN%7eeV7j)XTFn?*@chyfX z+<$LKM2^9!v7|z>b~Y1Uj(B3-sJ2U=!)Wv&^^nXsfI_Ro@n>| zf@mmq+hFS;pQ(5id-DP)O1^Bi7d>Hr5M*|Hp1)E;;~jCZU}L-FE>dNghThwrwYLEN zad!S@<7{7l*d^HS*$HC~{wW-MePc-McH?q1yc(Xjp%9a5a#IhpzrbTldW43k1!>N? z8elHsI%b(gC>l^ndEH_0jL$GT zIOn?)w2N(VclJ6~RD2kp7GDOZk0j1_8jOSsQbww8#HB#unQoo;3wHf%4KZ4ke=j60 z0L@3;OKSa_rH!)^pXMa>6|(o-n3>X%II6wMg^eg$CTlTuAvyiE-ekDX2~0!qi%I2R zwvRQMD`Z{kj(Zm2%9N353;(pi^`)g1 z?V;?Y6+WvhkZuWxlEWBw!dzyVzlV`bAB#FjGg+_dl^XI8wBW{0@p@|)o-rBmL3%rt z)+ux+PjvEI9tQ(&IPB(HzQ@Tvf$FQ@N}~+dFt#+WuI*oNjlgy&Zgy# z27b3lbe6omoWW-W#u?<@CNtoiV05pyY;G~~@Xe|;jPy*KSdkx3aFNE&JBd^Zu~{h5 zm6ZZ~A=(`#+l-AvKQ>16qT##Vths;PbZ)Iwl>#w9H7t96WBCd1r+~3yM1f^n zHPC|wA0j!yCBig1G!|T-?(kF`utX`#nmr^I^aWzkrFeS zSW32uCrF+4&#w!j+4S|^oEp);!m_TO==#>!_sy9|nV(c)Z1xxKr$nmqr}(gRqkrCK zD$=~7Nn`Kya%50$J~1$Gd}a5?;f~T_u=*8|lLeajn>iDH;}EPwfR_dVHXQ(H{=d4Cey_7AcVYjc^mN6J=Z{OgfH~o-xk>wCP)_QSZ=!CW zywW>&^4|ci@ed9=YMVj!SpSB^+)b6Bo$}BF?wzGeq_g@=dn+`1RUti|-R7B#`xKE^ zLPfmDi%cJvNz3gAlKK;AAMb4zg$jMNdb@OSekMspjgP$EiCx{l7?+vj6`F%x@4eWL z6>{6ot|A9W1!%k#SI2e-c-B*-hokznt4?LQ2OQX!jp-62mE&TZK{KGQ*-uA(z00+?ODH;%}f-j zM|LVTZYWLg+JwHWt9DvgaHyRz`a9=~#VeV_0kO}6MYM13Wwxy>{x+UKzg^C3+X+bQ zk7_F?9@Jzfv3qxyw3XjV?9bA!b~rU$ZPP~_sZ*L?K;lsJwaMrWYqX~*a{$k&kJ*vp zQEl-?H@BYwX!nYWI`v!epiJC#P+rmdpLL5zf< z-}k!%g5h~OUkiilak&>&wi zJNYiwH$O)Xl@7qz^1{|%d&0SVcA;CmmKJ=C{D96?$s>6e&&wc0JJuS_Ri#FW5d1BgX77_g8^K7Z&d{4D>=#^MJ zh+^yV2Ao@(diw||C5VAovW4Nal@T2U0Em z-IrugNAhc9VbxU9p{IP{=%c}5K6%15YqrW>v6D41(^jX$mSwatZG;ch@}b&ru#ONH z^G6PAqXb@1Kal*qF1G2w@X&vcL*^!Nmr^zKlr~(nJKzJktno=}g*>=x6RXiik#7C9 zkZ7iL#Ebf3tj0IOwoTOY1cwy9wZbReA1}$oV#Lo*J4D_izTyny;le3>eqiRv{*NRE z@EfN;o1W){UM6o2p&+fd>$O&NY4fzm&{;q;GWuZsHP1NB*4jT3IjnfWZ*YB~r)sH~ zs|`N66Pciw5J2oOg1_L_h9ro_PN|LOufI0V30L{b*XLM>BGfb)IxINCUvTMsBTW2V zAPgGu^-h{Ubu^E~+l9nxyqhkrNj70;Yszdl_n`krVvAffa#q8+1v}bCxbhlxX9~6O z1`^I6Djw5gBU6>B($;;|Eogm}hKh6+u3xSg_>*7hv=+H#y}i;IcBjSczQ`_t^|#~W z){2gv#w=k_E?6tDR37Bs^m8}qLPg$?hIHFa7|QqyONXfnpJn-q;7m{gKP5cwAv$LL zx%~5t8v49tgTD}R%R^Lh_8N9_SC(Yp*uab-GWR1!7guM-f1;5~{*Z*(>j^gtbgyq# zW1WwfNPrg@BG(mO?p*#ESa~gt9P;p|Hb>Z`j)e@Vh@Jc;I^pv*VV%iw@g&1;eV(IQ zNI%|mD1@_KY|~ka+z7HuT9^B`J|uS{*XuuMWLJzTq;e0DqUtGFI=AwNV87I_8+ilP zf0QPeFT%t9pNq9LC9Er~=)kkvKA%k-+eFt@c^tL%gzr2$9oFrzl)2v2POwWXReg;gw> zy+vJ4G6Bckw7CCZ;f*IeheiZ^Mpt^9=q^pl@QlR@Mx*@s3J_hvH&0EIMW=Oppz#bM z(bE0TWkT6luflJitBzh*Uu694*22gq5BfzQ!!qdN*C`g0`PS}Pq3(ouUu?m$PDfn6 zpp`c`B@>#Lsxw($j_&251h)0-grfRR4N^c zxfwOlWgmKRU%s@3JgSqGUw=uf8i5@+nXdKwL)BOJlJ~g<5*Zb2*k5$%v6~znzxHWt zY4F`&wGRcSsb5yrK0h(v^L~SIzD()=L3{ka$_MDp9w2l7`l_UGr*V-nPpHpmhGo9# zlC@n@?qaL`5XOpPuj1+qQNs{&p?PT2kJAOH^qAy4@8mBo!SL;34YlYdm1&q(r`#9F_euV0q*D?%cWat6JlzL?ub5{Ee#YPOa{c+qV|>u=TS$7maW7 z+64(g1~m*YavR3dysxyG!POm*W|ths8{dj=M4&gSJ(RLu%oBI&+q_m>bSrK`NzRl- zG_}@@M0bEP93vYjvU$RP%^YnN8nSnBcZT^ONgy!>z2SrIjpUf_>G4i#`|8lJ>A68D z^Q*>c*2i32d#2qmfps5!#;_pg&#_viJ;QQYuD$U0NWnpMtEK! z$BSfJuK{n-1$4|+NY7$o6hbE{(b93h2~8_KZUbJ8za2Qx(o~@IXxp^aS(IY)Y-z!K z@a><3L7dkQtPDYLUiCC+Y9ck8l^+jT-Z!!5Ap`RABJk>xYUtTX&ueIqBe@k@66V!7 z=TN1fI3EbdNH-+A6JoqwM-5Ay(H@zd0n^zgCd#2#P+_c*8t1{v%lC;=Msi2*^KrU) zilq6XBX3}u4_I6%RQ)kPIEAt&iiQts;@4kq_WkpY`kIoRTT!I_3*JhW+Ib6}2 zlt%rG1WNCQ5MQA|>R2s}o;?e@BhxGopPVZUZdhIy%U^{u3b$8%~%D z4B_^VfP8VdoXsfHmI}xVNCorKHj19dISke_TSxx!WNF1p7aWb2DF4FC?O_T4R}a0i zK0nH+!xYBBXh6RVxfUpEM_wRM1pC`zvd~njFj^k`y3W1D?R9UyerC1J1XP>X***_^ zT_-1!$qyQnMc*Q{6plnpJG7TUg{e_vdI|P)X=a03JOxFQZvF1q2vu4GAu<+H_rstYq;k;(NX~hob+{Xt#C%KIZvQ_(}0t$2Or|e(eZ>M z&2fapXmB6RK}utS!(gyd?``ao5xsM<>WyT;sm0zh61X+HAdGSXGqI4?a zAs6m9ftR)vV3Z^b;L;T7WJ3ud`M@24SSJF zd(twIucXbTq-Ia+?Vi@mdzUbEF&XbuD8;|49+fDB3^2RC7$!@iN#XO@+1DK+w}0av zM-N9Oe0fIjt`7EJos)>g8c?90?5S|xgKM6^#gheECQ5I>L$RC7##*T)?}dG@iHUc6 zABiLL;9yWk8w4%C5d&EC@2x-OdbXYlv zCtNouM*TrU!xd{uxW<8mEhvLk48U~6-RIhvR9Npi8Mog27TPPAAE@*zM@z+cYE>>Y z-2xT1+69p!G(^!^S~CZ++%87kSrc#x0x02-Cfvaao$Z1_z>OZaFD4)-i7=G4Q=?wGBIw#Ozal| zM*>3y&=a$;9Q++oF42woP{Y0!m9TRYF!8-6WwlL;d=YJp2_>ob+neu6b$|*hG-_$8 zIQ*l{dE0|u+<-E98V@LI{ojD8|7$ipl;bE&Vf(D@b@DZ*kpFF%LPqX5O!41|ejJA> zj>8nkVT$801r#_r4pSV5DUQPw$6<=&FvW40;y6rk9Hux9QyhmWj>8oHi!KfPx55;T;Ep0wR}^WKyI8kToXmf z52qETKnpo8F@oKSG1;G5hJTj7ZD-fdE4}jb$~78m`2GXRDdz>wH@4rNeJrW#QCi)! z_?QRXq&`3PVZtGIe9-$Sfb##oAC1Iw&Z-g$XM*m*bUz`>e})+Ob@N5U@BhAB$NdK1 z{BfhVxh(V@I%K_gO!wJl+jHIh#jJB7%bjHNv3ccJI7eKoeZZfoZ_J^GuNi&qcVVwJ z`NqjjS#@guUsoK@Ju@|U+c})ab4b6@ao*m~c*v*6C}Nx17

(;!a4@Qf;n}y`Ohe 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 diff --git a/app/src/assets/scss/base.scss b/app/src/assets/scss/base.scss index 132f9327e62..9f82d3d4042 100644 --- a/app/src/assets/scss/base.scss +++ b/app/src/assets/scss/base.scss @@ -318,12 +318,12 @@ html { background-color: var(--b3-theme-primary-lightest); &__top { - background-color: var(--b3-theme-primary-lightest); + border-radius: 0; box-shadow: 0 -2px 0 var(--b3-theme-primary-lighter), inset 0 2px 0 var(--b3-theme-primary-lighter); } &__bottom { - background-color: var(--b3-theme-primary-lightest); + border-radius: 0; box-shadow: 0 2px 0 var(--b3-theme-primary-lighter), inset 0px -2px 0 var(--b3-theme-primary-lighter); } } diff --git a/app/src/assets/scss/business/_av.scss b/app/src/assets/scss/business/_av.scss index 46fc334fd06..c10445724a7 100644 --- a/app/src/assets/scss/business/_av.scss +++ b/app/src/assets/scss/business/_av.scss @@ -224,7 +224,6 @@ flex-shrink: 0; border-right: 1px solid var(--b3-theme-surface-lighter); display: flex; - align-items: center; transition: var(--b3-transition); [data-type="block-ref"], @@ -233,11 +232,19 @@ position: absolute; right: 5px; font-size: 85%; + top: 8px; + } + + &--select { + background-color: var(--b3-theme-primary-lightest); + box-shadow: 2px 2px 0 var(--b3-theme-primary-lighter) inset, -2px -2px 0px var(--b3-theme-primary-lighter) inset; + border-radius: var(--b3-border-radius); } .block__icon { position: absolute; right: 5px; + top: 5px; } &[data-wrap=true] { @@ -282,22 +289,17 @@ .b3-chip { margin: 1px 2px; + padding: 2px 6px; } &--url { text-decoration: underline var(--b3-border-color); } - &--date { - display: flex; - align-items: center; - - svg { - margin: 0 5px; - height: 10px; - width: 10px; - flex-shrink: 0; - } + & > .av__cellicon { + margin: 0 5px; + height: 10px; + width: 10px; } } diff --git a/app/src/assets/scss/component/_typography.scss b/app/src/assets/scss/component/_typography.scss index a96f22bf69c..afbb8511582 100644 --- a/app/src/assets/scss/component/_typography.scss +++ b/app/src/assets/scss/component/_typography.scss @@ -97,7 +97,7 @@ kbd, span[data-type~="kbd"] { padding: 2px 4px; - font: 75% Consolas, "Liberation Mono", Menlo, Courier, monospace; + font: 75% Consolas, "Liberation Mono", Menlo, Courier, monospace, var(--b3-font-family); line-height: 1; color: var(--b3-theme-on-surface); vertical-align: middle; diff --git a/app/src/assets/scss/protyle/_toolbar.scss b/app/src/assets/scss/protyle/_toolbar.scss index 50919530d1d..081a23e50ab 100644 --- a/app/src/assets/scss/protyle/_toolbar.scss +++ b/app/src/assets/scss/protyle/_toolbar.scss @@ -1,7 +1,7 @@ .protyle { &-toolbar { background-color: var(--b3-theme-background); - z-index: 3; + z-index: 4; // https://ld246.com/article/1697096086017 position: fixed; box-shadow: var(--b3-point-shadow); border-radius: var(--b3-border-radius); diff --git a/app/src/config/exportConfig.ts b/app/src/config/exportConfig.ts index 3ee90a9e9c4..690af327a05 100644 --- a/app/src/config/exportConfig.ts +++ b/app/src/config/exportConfig.ts @@ -213,7 +213,7 @@ export const exportConfig = { const localPath = await ipcRenderer.invoke(Constants.SIYUAN_GET, { cmd: "showOpenDialog", defaultPath: window.siyuan.config.system.homeDir, - properties: ["openFile"], + properties: ["openFile", "showHiddenFiles"], }); if (localPath.filePaths.length === 0) { pandocBinElement.value = window.siyuan.config.export.pandocBin; diff --git a/app/src/layout/status.ts b/app/src/layout/status.ts index 6a63657f6ce..951016fe03e 100644 --- a/app/src/layout/status.ts +++ b/app/src/layout/status.ts @@ -195,6 +195,9 @@ export const renderStatusbarCounter = (stat: { imageCount: number, refCount: number }) => { + if(!stat) { + return; + } let html = `${window.siyuan.languages.runeCount} ${stat.runeCount} ${window.siyuan.languages.wordCount} ${stat.wordCount}`; if (0 < stat.linkCount) { diff --git a/app/src/menus/protyle.ts b/app/src/menus/protyle.ts index 9933407381c..0fe28bb038c 100644 --- a/app/src/menus/protyle.ts +++ b/app/src/menus/protyle.ts @@ -45,6 +45,7 @@ import {hideElements} from "../protyle/ui/hideElements"; import {emitOpenMenu} from "../plugin/EventBus"; import {openMobileFileById} from "../mobile/editor"; import {openBacklink, openGraph} from "../layout/dock/util"; +import {updateHeader} from "../protyle/render/av/row"; export const fileAnnotationRefMenu = (protyle: IProtyle, refElement: HTMLElement) => { const nodeElement = hasClosestBlock(refElement); @@ -1663,8 +1664,14 @@ export const setFold = (protyle: IProtyle, nodeElement: Element, isOpen?: boolea focusBlock(nodeElement, undefined, false); } } - nodeElement.querySelectorAll(".img--select").forEach((item) => { - item.classList.remove("img--select"); + nodeElement.querySelectorAll(".img--select, .av__cell--select, .av__row--select").forEach((item:HTMLElement) => { + if (item.classList.contains("av__row--select")) { + item.classList.remove("av__row--select"); + item.querySelector(".av__firstcol use").setAttribute("xlink:href", "#iconUncheck"); + updateHeader(item); + } else { + item.classList.remove("img--select", "av__cell--select"); + } }); } const id = nodeElement.getAttribute("data-node-id"); diff --git a/app/src/protyle/gutter/index.ts b/app/src/protyle/gutter/index.ts index d1c6aef9b7a..b5a4649ccbf 100644 --- a/app/src/protyle/gutter/index.ts +++ b/app/src/protyle/gutter/index.ts @@ -38,6 +38,7 @@ import {appearanceMenu} from "../toolbar/Font"; import {setPosition} from "../../util/setPosition"; import {avRender} from "../render/av/render"; import {emitOpenMenu} from "../../plugin/EventBus"; +import {resizeAV} from "../util/resize"; export class Gutter { public element: HTMLElement; @@ -1614,7 +1615,12 @@ export class Gutter { accelerator: window.siyuan.config.keymap.editor.general.alignLeft.custom, click: () => { this.genClick(nodeElements, protyle, (e: HTMLElement) => { - e.style.textAlign = "left"; + if (e.classList.contains("av")) { + e.style.margin = "" + resizeAV(e); + } else { + e.style.textAlign = "left"; + } }); } }, { @@ -1623,7 +1629,12 @@ export class Gutter { accelerator: window.siyuan.config.keymap.editor.general.alignCenter.custom, click: () => { this.genClick(nodeElements, protyle, (e: HTMLElement) => { - e.style.textAlign = "center"; + if (e.classList.contains("av")) { + e.style.margin = "0 auto"; + resizeAV(e); + } else { + e.style.textAlign = "center"; + } }); } }, { @@ -1632,7 +1643,12 @@ export class Gutter { accelerator: window.siyuan.config.keymap.editor.general.alignRight.custom, click: () => { this.genClick(nodeElements, protyle, (e: HTMLElement) => { - e.style.textAlign = "right"; + if (e.classList.contains("av")) { + e.style.margin = "0 0 0 auto"; + resizeAV(e); + } else { + e.style.textAlign = "right"; + } }); } }, { @@ -1658,7 +1674,9 @@ export class Gutter { icon: "iconRtl", click: () => { this.genClick(nodeElements, protyle, (e: HTMLElement) => { - e.style.direction = "rtl"; + if (!e.classList.contains("av")) { + e.style.direction = "rtl"; + } }); } }, { @@ -1668,8 +1686,13 @@ export class Gutter { icon: "iconTrashcan", click: () => { this.genClick(nodeElements, protyle, (e: HTMLElement) => { - e.style.textAlign = ""; - e.style.direction = ""; + if (e.classList.contains("av")) { + e.style.margin = ""; + resizeAV(e); + } else { + e.style.textAlign = ""; + e.style.direction = ""; + } }); } }] @@ -1685,6 +1708,7 @@ export class Gutter { this.genClick(nodeElements, protyle, (e: HTMLElement) => { e.style.width = item; e.style.flex = "none"; + resizeAV(e); }); } }); @@ -1724,12 +1748,13 @@ export class Gutter { }); }); rangeElement.addEventListener("change", () => { - nodeElements.forEach((e) => { + nodeElements.forEach((e: HTMLElement) => { operations.push({ action: "update", id: e.getAttribute("data-node-id"), data: e.outerHTML }); + resizeAV(e); }); transaction(protyle, operations, undoOperations); window.siyuan.menus.menu.remove(); @@ -1746,6 +1771,7 @@ export class Gutter { if (e.style.width) { e.style.width = ""; e.style.flex = ""; + resizeAV(e); } }); } diff --git a/app/src/protyle/index.ts b/app/src/protyle/index.ts index 35477bec432..113504cd862 100644 --- a/app/src/protyle/index.ts +++ b/app/src/protyle/index.ts @@ -113,7 +113,9 @@ export class Protyle { break; case "transactions": data.data[0].doOperations.forEach((item: IOperation) => { - if (!this.protyle.preview.element.classList.contains("fn__none")) { + if (!this.protyle.preview.element.classList.contains("fn__none") && + item.action !== "updateAttrs" // 预览模式下点击只读 + ) { this.protyle.preview.render(this.protyle); } else { onTransaction(this.protyle, item, false); diff --git a/app/src/protyle/render/av/action.ts b/app/src/protyle/render/av/action.ts index 9d93b18721a..e6d114c8868 100644 --- a/app/src/protyle/render/av/action.ts +++ b/app/src/protyle/render/av/action.ts @@ -4,7 +4,8 @@ import {transaction} from "../../wysiwyg/transaction"; import {openEditorTab} from "../../../menus/util"; import {copySubMenu} from "../../../menus/commonMenuItem"; import {openCalcMenu, popTextCell} from "./cell"; -import {getColIconByType, showColMenu, updateHeader} from "./col"; +import {getColIconByType, showColMenu} from "./col"; +import {insertAttrViewBlockAnimation, updateHeader} from "./row"; import {emitOpenMenu} from "../../../plugin/EventBus"; import {addCol} from "./addCol"; import {openMenuPanel} from "./openMenuPanel"; @@ -13,12 +14,27 @@ import {focusByRange} from "../../util/selection"; import {writeText} from "../../util/compatibility"; import {showMessage} from "../../../dialog/message"; import {previewImage} from "../../preview/image"; +import {isLocalPath, pathPosix} from "../../../util/pathName"; +import {Constants} from "../../../constants"; +import {openAsset} from "../../../editor/util"; +import {getSearch, isMobile} from "../../../util/functions"; +import {unicode2Emoji} from "../../../emoji"; +import {selectRow} from "./row"; +import * as dayjs from "dayjs"; export const avClick = (protyle: IProtyle, event: MouseEvent & { target: HTMLElement }) => { const blockElement = hasClosestBlock(event.target); if (!blockElement) { return false; } + if (event.shiftKey) { + const rowElement = hasClosestByClassName(event.target, "av__row"); + if (rowElement && !rowElement.classList.contains("av__row--header")) { + selectRow(rowElement.querySelector(".av__firstcol"), "toggle"); + return true; + } + } + const copyElement = hasClosestByAttribute(event.target, "data-type", "copy"); if (copyElement) { writeText(copyElement.previousElementSibling.textContent.trim()); @@ -47,7 +63,13 @@ export const avClick = (protyle: IProtyle, event: MouseEvent & { target: HTMLEle const gutterElement = hasClosestByClassName(event.target, "av__gutters"); if (gutterElement) { - avContextmenu(protyle, event, gutterElement); + const gutterRect = gutterElement.getBoundingClientRect() + avContextmenu(protyle, gutterElement.parentElement, { + x: gutterRect.left, + y: gutterRect.bottom, + w: gutterRect.width, + h: gutterRect.height + }); event.preventDefault(); event.stopPropagation(); return true; @@ -56,30 +78,7 @@ export const avClick = (protyle: IProtyle, event: MouseEvent & { target: HTMLEle const firstcolElement = hasClosestByClassName(event.target, "av__firstcol"); if (firstcolElement) { window.siyuan.menus.menu.remove(); - const rowElement = firstcolElement.parentElement; - const useElement = firstcolElement.querySelector(".icon__check use"); - if (rowElement.classList.contains("av__row--header")) { - if ("#iconCheck" === useElement.getAttribute("xlink:href")) { - rowElement.parentElement.querySelectorAll(".av__row .av__firstcol").forEach(item => { - item.querySelector(".icon__check use").setAttribute("xlink:href", "#iconUncheck"); - item.parentElement.classList.remove("av__row--select"); - }); - } else { - rowElement.parentElement.querySelectorAll(".av__row .av__firstcol").forEach(item => { - item.querySelector(".icon__check use").setAttribute("xlink:href", "#iconCheck"); - item.parentElement.classList.add("av__row--select"); - }); - } - } else { - if (useElement.getAttribute("xlink:href") === "#iconUncheck") { - firstcolElement.parentElement.classList.add("av__row--select"); - useElement.setAttribute("xlink:href", "#iconCheck"); - } else { - firstcolElement.parentElement.classList.remove("av__row--select"); - useElement.setAttribute("xlink:href", "#iconUncheck"); - } - } - updateHeader(rowElement); + selectRow(firstcolElement, "toggle"); event.preventDefault(); event.stopPropagation(); return true; @@ -127,7 +126,16 @@ export const avClick = (protyle: IProtyle, event: MouseEvent & { target: HTMLEle } else if (linkElement.classList.contains("b3-chip")) { linkAddress = linkElement.dataset.url; } - window.open(linkAddress); + const suffix = pathPosix().extname(linkAddress); + if (isLocalPath(linkAddress) && !isMobile() && ( + [".pdf"].concat(Constants.SIYUAN_ASSETS_AUDIO).concat(Constants.SIYUAN_ASSETS_VIDEO).includes(suffix) && ( + suffix !== ".pdf" || (suffix === ".pdf" && !linkAddress.startsWith("file://")) + ) + )) { + openAsset(protyle.app, linkAddress.trim(), parseInt(getSearch("page", linkAddress)), "right"); + } else { + window.open(linkAddress); + } event.preventDefault(); event.stopPropagation(); return true; @@ -162,11 +170,17 @@ export const avClick = (protyle: IProtyle, event: MouseEvent & { target: HTMLEle const cellElement = hasClosestByClassName(event.target, "av__cell"); if (cellElement && !cellElement.parentElement.classList.contains("av__row--header")) { - cellElement.parentElement.parentElement.querySelectorAll(".av__row--select").forEach(item => { - item.querySelector(".av__firstcol .icon__check use").setAttribute("xlink:href", "#iconUncheck"); - item.classList.remove("av__row--select"); - }); - popTextCell(protyle, [cellElement]); + const type = cellElement.parentElement.parentElement.firstElementChild.querySelector(`[data-col-id="${cellElement.getAttribute("data-col-id")}"]`).getAttribute("data-dtype") as TAVCol; + if (type === "updated" || type === "created" || (type === "block" && !cellElement.getAttribute("data-detached"))) { + selectRow(cellElement.parentElement.querySelector(".av__firstcol"), "toggle"); + } else { + cellElement.parentElement.parentElement.querySelectorAll(".av__row--select").forEach(item => { + item.querySelector(".av__firstcol .icon__check use").setAttribute("xlink:href", "#iconUncheck"); + item.classList.remove("av__row--select"); + }); + updateHeader(cellElement.parentElement); + popTextCell(protyle, [cellElement]); + } event.preventDefault(); event.stopPropagation(); return true; @@ -206,11 +220,7 @@ export const avClick = (protyle: IProtyle, event: MouseEvent & { target: HTMLEle return false; }; -export const avContextmenu = (protyle: IProtyle, event: MouseEvent & { detail: any }, target: HTMLElement) => { - const rowElement = hasClosestByClassName(target, "av__row"); - if (!rowElement) { - return false; - } +export const avContextmenu = (protyle: IProtyle, rowElement: HTMLElement, position: IPosition) => { if (rowElement.classList.contains("av__row--header")) { return false; } @@ -218,9 +228,6 @@ export const avContextmenu = (protyle: IProtyle, event: MouseEvent & { detail: a if (!blockElement) { return false; } - event.preventDefault(); - event.stopPropagation(); - if (!rowElement.classList.contains("av__row--select")) { blockElement.querySelectorAll(".av__row--select").forEach(item => { item.classList.remove("av__row--select"); @@ -288,10 +295,12 @@ export const avContextmenu = (protyle: IProtyle, event: MouseEvent & { detail: a } }); } - if (!hideBlock) { + const type = cellElement.getAttribute("data-dtype") as TAVCol; + if (!hideBlock && !["updated", "created"].includes(type)) { + const icon = cellElement.dataset.icon; editAttrSubmenu.push({ - icon: getColIconByType(cellElement.getAttribute("data-dtype") as TAVCol), - label: cellElement.textContent.trim(), + iconHTML: icon ? unicode2Emoji(icon, "b3-menu__icon", true) : ``, + label: cellElement.querySelector(".av__celltext").textContent.trim(), click() { popTextCell(protyle, selectElements); } @@ -310,34 +319,45 @@ export const avContextmenu = (protyle: IProtyle, event: MouseEvent & { detail: a type: "open-menu-av", detail: { protyle, - element: hasClosestByClassName(target, "av__cell"), + element: blockElement, + selectRowElements: rowElements, }, separatorPosition: "top", }); } - menu.open({ - x: event.clientX, - y: event.clientY, - }); + menu.open(position); return true; }; export const updateAVName = (protyle: IProtyle, blockElement: Element) => { const avId = blockElement.getAttribute("data-av-id"); + const id = blockElement.getAttribute("data-node-id"); const nameElement = blockElement.querySelector(".av__title") as HTMLElement; - if (nameElement.textContent.trim() === nameElement.dataset.title.trim()) { + const newData = nameElement.textContent.trim(); + if (newData === nameElement.dataset.title.trim()) { return; } + const newUpdated = dayjs().format("YYYYMMDDHHmmss") transaction(protyle, [{ action: "setAttrViewName", id: avId, - data: nameElement.textContent.trim(), + data: newData, + }, { + action: "doUpdateUpdated", + id, + data: newUpdated, }], [{ action: "setAttrViewName", id: avId, - name: nameElement.dataset.title, + data: nameElement.dataset.title, + }, { + action: "doUpdateUpdated", + id, + data: blockElement.getAttribute("updated") }]); - nameElement.dataset.title = nameElement.textContent.trim(); + blockElement.setAttribute("updated", newUpdated); + nameElement.dataset.title = newData; + blockElement.querySelector(".layout-tab-bar .item__text").textContent = newData; }; export const updateAttrViewCellAnimation = (cellElement: HTMLElement) => { @@ -350,20 +370,3 @@ export const removeAttrViewColAnimation = (blockElement: Element, id: string) => item.remove(); }); }; - -export const insertAttrViewBlockAnimation = (blockElement: Element, size: number, previousId: string, avId?: string) => { - const previousElement = blockElement.querySelector(`.av__row[data-id="${previousId}"]`) || blockElement.querySelector(".av__row--header"); - let colHTML = ""; - previousElement.querySelectorAll(".av__cell").forEach((item: HTMLElement) => { - colHTML += `

`; - }); - - let html = ""; - new Array(size).fill(1).forEach(() => { - html += `
-
- ${colHTML} -
`; - }); - previousElement.insertAdjacentHTML("afterend", html); -}; diff --git a/app/src/protyle/render/av/asset.ts b/app/src/protyle/render/av/asset.ts index a3ebbeeeefc..c8eb6f47060 100644 --- a/app/src/protyle/render/av/asset.ts +++ b/app/src/protyle/render/av/asset.ts @@ -68,11 +68,11 @@ export const getAssetHTML = (data: IAVTable, cellElements: HTMLElement[]) => { } let contentHTML; if (item.type === "image") { - contentHTML = ` + contentHTML = ` `; } else { - contentHTML = `${item.name}`; + contentHTML = `${item.name}`; } html += ` `; if (colData.options && colData.options.length > 0) { html += ` `; colData.options.forEach(item => { html += ` `; } return `
@@ -225,7 +225,7 @@ export const bindEditEvent = (options: { protyle: IProtyle, data: IAV, menuEleme } if (event.key === "Escape") { options.menuElement.parentElement.remove(); - } else if (event.key === "Enter") { + } else if (event.key === "Enter" && !event.shiftKey) { tplElement.dispatchEvent(new CustomEvent("blur")); options.menuElement.parentElement.remove(); } @@ -281,7 +281,7 @@ export const getColIconByType = (type: TAVCol) => { case "text": return "iconAlignLeft"; case "block": - return "iconParagraph"; + return "iconKey"; case "number": return "iconNumber"; case "select": @@ -306,35 +306,6 @@ export const getColIconByType = (type: TAVCol) => { } }; -export const updateHeader = (rowElement: HTMLElement) => { - const blockElement = hasClosestBlock(rowElement); - if (!blockElement) { - return; - } - const selectCount = rowElement.parentElement.querySelectorAll(".av__row--select:not(.av__row--header)").length; - const diffCount = rowElement.parentElement.childElementCount - 3 - selectCount; - const headElement = rowElement.parentElement.firstElementChild; - const headUseElement = headElement.querySelector("use"); - const counterElement = blockElement.querySelector(".av__counter"); - const avHeadElement = blockElement.querySelector(".av__header") as HTMLElement; - if (diffCount === 0 && rowElement.parentElement.childElementCount - 3 !== 0) { - headElement.classList.add("av__row--select"); - headUseElement.setAttribute("xlink:href", "#iconCheck"); - } else if (diffCount === rowElement.parentElement.childElementCount - 3) { - headElement.classList.remove("av__row--select"); - headUseElement.setAttribute("xlink:href", "#iconUncheck"); - counterElement.classList.add("fn__none"); - avHeadElement.style.position = ""; - return; - } else if (diffCount > 0) { - headElement.classList.add("av__row--select"); - headUseElement.setAttribute("xlink:href", "#iconIndeterminateCheck"); - } - counterElement.classList.remove("fn__none"); - counterElement.innerHTML = `${selectCount} selected`; - avHeadElement.style.position = "sticky"; -}; - export const addAttrViewColAnimation = (options: { blockElement: Element, protyle: IProtyle, @@ -364,7 +335,7 @@ export const addAttrViewColAnimation = (options: {
`; } else { - html = '
'; + html = '
'; } previousElement.insertAdjacentHTML("afterend", html); }); @@ -400,7 +371,7 @@ export const showColMenu = (protyle: IProtyle, blockElement: Element, cellElemen menu.addItem({ iconHTML: `${cellElement.dataset.icon ? unicode2Emoji(cellElement.dataset.icon) : ``}`, type: "readonly", - label: ``, + label: ``, bind(element) { const iconElement = element.querySelector(".block__icon") as HTMLElement; iconElement.setAttribute("data-icon", cellElement.dataset.icon); diff --git a/app/src/protyle/render/av/filter.ts b/app/src/protyle/render/av/filter.ts index 032a1d13d9d..862896911c3 100644 --- a/app/src/protyle/render/av/filter.ts +++ b/app/src/protyle/render/av/filter.ts @@ -141,7 +141,6 @@ export const setFilter = (options: { case "text": case "url": case "phone": - case "template": case "email": selectHTML = ` @@ -152,6 +151,20 @@ export const setFilter = (options: { `; break; + case "template": + selectHTML = ` + + + + + + + + + + +`; + break; case "date": case "created": case "updated": @@ -309,7 +322,7 @@ export const addFilter = (options: { if (!hasFilter && column.type !== "mAsset") { menu.addItem({ label: column.name, - iconHTML: `${column.icon ? unicode2Emoji(column.icon) : ``}`, + iconHTML: column.icon ? unicode2Emoji(column.icon, "b3-menu__icon", true) : ``, click: () => { const oldFilters = Object.assign([], options.data.view.filters); const cellValue = genCellValue(column.type, ""); diff --git a/app/src/protyle/render/av/keydown.ts b/app/src/protyle/render/av/keydown.ts new file mode 100644 index 00000000000..cfd2be3464f --- /dev/null +++ b/app/src/protyle/render/av/keydown.ts @@ -0,0 +1,140 @@ +import {matchHotKey} from "../../util/hotKey"; +import {selectRow} from "./row"; +import {cellScrollIntoView, popTextCell} from "./cell"; +import {avContextmenu} from "./action"; + +export const avKeydown = (event: KeyboardEvent, nodeElement: HTMLElement, protyle: IProtyle) => { + if (!nodeElement.classList.contains("av") || !window.siyuan.menus.menu.element.classList.contains("fn__none")) { + return false; + } + if (event.isComposing) { + return true; + } + // 避免浏览器默认快捷键 + if (matchHotKey("⌘B", event) || matchHotKey("⌘I", event) || matchHotKey("⌘U", event)) { + event.preventDefault(); + return true; + } + const selectCellElement = nodeElement.querySelector(".av__cell--select") as HTMLElement; + if (selectCellElement) { + if (event.key === "Escape") { + selectCellElement.classList.remove("av__cell--select"); + selectRow(selectCellElement.parentElement.querySelector(".av__firstcol"), "select"); + event.preventDefault(); + return true; + } + if (event.key === "Enter") { + popTextCell(protyle, [selectCellElement]); + event.preventDefault(); + return true; + } + let newCellElement + if (event.key === "ArrowLeft") { + const previousRowElement = selectCellElement.parentElement.previousElementSibling + if (selectCellElement.previousElementSibling && selectCellElement.previousElementSibling.classList.contains("av__cell")) { + newCellElement = selectCellElement.previousElementSibling + } else if (previousRowElement && !previousRowElement.classList.contains("av__row--header")) { + newCellElement = previousRowElement.lastElementChild.previousElementSibling + } + if (newCellElement) { + selectCellElement.classList.remove("av__cell--select"); + newCellElement.classList.add("av__cell--select"); + cellScrollIntoView(nodeElement, newCellElement.getBoundingClientRect()); + } + event.preventDefault(); + return true; + } + if (event.key === "ArrowRight") { + const nextRowElement = selectCellElement.parentElement.nextElementSibling + if (selectCellElement.nextElementSibling && selectCellElement.nextElementSibling.classList.contains("av__cell")) { + newCellElement = selectCellElement.nextElementSibling + } else if (nextRowElement && !nextRowElement.classList.contains("av__row--footer")) { + newCellElement = nextRowElement.querySelector(".av__cell") + } + if (newCellElement) { + selectCellElement.classList.remove("av__cell--select"); + newCellElement.classList.add("av__cell--select"); + cellScrollIntoView(nodeElement, newCellElement.getBoundingClientRect()); + } + event.preventDefault(); + return true; + } + if (event.key === "ArrowUp") { + const previousRowElement = selectCellElement.parentElement.previousElementSibling + if (previousRowElement && !previousRowElement.classList.contains("av__row--header")) { + newCellElement = previousRowElement.querySelector(`.av__cell[data-col-id="${selectCellElement.dataset.colId}"]`) + } + if (newCellElement) { + selectCellElement.classList.remove("av__cell--select"); + newCellElement.classList.add("av__cell--select"); + cellScrollIntoView(nodeElement, newCellElement.getBoundingClientRect()); + } + event.preventDefault(); + return true; + } + if (event.key === "ArrowDown") { + const nextRowElement = selectCellElement.parentElement.nextElementSibling + if (nextRowElement && !nextRowElement.classList.contains("av__row--footer")) { + newCellElement = nextRowElement.querySelector(`.av__cell[data-col-id="${selectCellElement.dataset.colId}"]`) + } + if (newCellElement) { + selectCellElement.classList.remove("av__cell--select"); + newCellElement.classList.add("av__cell--select"); + cellScrollIntoView(nodeElement, newCellElement.getBoundingClientRect()); + } + event.preventDefault(); + return true; + } + } + + const selectRowElements = nodeElement.querySelectorAll(".av__row--select:not(.av__row--header)"); + if (selectRowElements.length > 0) { + if (matchHotKey("⌘/", event)) { + event.stopPropagation(); + event.preventDefault(); + avContextmenu(protyle, selectRowElements[0] as HTMLElement, { + x: nodeElement.querySelector(".layout-tab-bar").getBoundingClientRect().left, + y: selectRowElements[0].getBoundingClientRect().bottom + }); + return true; + } + if (event.key === "Escape") { + event.preventDefault(); + selectRow(selectRowElements[0].querySelector(".av__firstcol"), "unselectAll"); + return true; + } + if (event.key === "Enter") { + selectRow(selectRowElements[0].querySelector(".av__firstcol"), "unselectAll"); + popTextCell(protyle, [selectRowElements[0].querySelector(".av__cell")]); + event.preventDefault(); + return true; + } + // TODO event.shiftKey + if (event.key === "ArrowUp") { + const previousRowElement = selectRowElements[0].previousElementSibling + selectRow(selectRowElements[0].querySelector(".av__firstcol"), "unselectAll"); + if (previousRowElement && !previousRowElement.classList.contains("av__row--header")) { + selectRow(previousRowElement.querySelector(".av__firstcol"), "select"); + cellScrollIntoView(nodeElement, previousRowElement.getBoundingClientRect(), true); + } else { + nodeElement.classList.add("protyle-wysiwyg--select") + } + event.preventDefault(); + return true; + } + if (event.key === "ArrowDown") { + const nextRowElement = selectRowElements[selectRowElements.length - 1].nextElementSibling + selectRow(selectRowElements[0].querySelector(".av__firstcol"), "unselectAll"); + if (nextRowElement && !nextRowElement.classList.contains("av__row--add")) { + selectRow(nextRowElement.querySelector(".av__firstcol"), "select"); + cellScrollIntoView(nodeElement, nextRowElement.getBoundingClientRect(), true); + } else { + nodeElement.classList.add("protyle-wysiwyg--select") + } + event.preventDefault(); + return true; + } + } + return false; +} + diff --git a/app/src/protyle/render/av/openMenuPanel.ts b/app/src/protyle/render/av/openMenuPanel.ts index 0406a6fcf64..167f59541f9 100644 --- a/app/src/protyle/render/av/openMenuPanel.ts +++ b/app/src/protyle/render/av/openMenuPanel.ts @@ -13,8 +13,11 @@ import {removeAttrViewColAnimation, updateAttrViewCellAnimation} from "./action" import {addAssetLink, bindAssetEvent, editAssetItem, getAssetHTML, updateAssetCell} from "./asset"; import {Constants} from "../../../constants"; import {hideElements} from "../../ui/hideElements"; -import {pathPosix} from "../../../util/pathName"; +import {isLocalPath, pathPosix} from "../../../util/pathName"; import {openEmojiPanel, unicode2Emoji} from "../../../emoji"; +import {getSearch, isMobile} from "../../../util/functions"; +import {openAsset} from "../../../editor/util"; +import {previewImage} from "../../preview/image"; export const openMenuPanel = (options: { protyle: IProtyle, @@ -741,6 +744,23 @@ export const openMenuPanel = (options: { event.preventDefault(); event.stopPropagation(); break; + } else if (type === "openAssetItem") { + const assetLink = target.parentElement.dataset.content; + const suffix = pathPosix().extname(assetLink); + if (isLocalPath(assetLink) && !isMobile() && ( + [".pdf"].concat(Constants.SIYUAN_ASSETS_AUDIO).concat(Constants.SIYUAN_ASSETS_VIDEO).includes(suffix) && ( + suffix !== ".pdf" || ( suffix === ".pdf" && !assetLink.startsWith("file://")) + ) + )) { + openAsset(options.protyle.app, assetLink.trim(), parseInt(getSearch("page", assetLink)), "right"); + } else if (Constants.SIYUAN_ASSETS_IMAGE.includes(suffix)) { + previewImage(assetLink); + } else { + window.open(assetLink); + } + event.preventDefault(); + event.stopPropagation(); + break; } else if (type === "editAssetItem") { editAssetItem(options.protyle, data, options.cellElements, target.parentElement); event.preventDefault(); diff --git a/app/src/protyle/render/av/render.ts b/app/src/protyle/render/av/render.ts index a8d2666523e..0e4f1791e0c 100644 --- a/app/src/protyle/render/av/render.ts +++ b/app/src/protyle/render/av/render.ts @@ -4,6 +4,8 @@ import {Constants} from "../../../constants"; import {getCalcValue} from "./cell"; import * as dayjs from "dayjs"; import {unicode2Emoji} from "../../../emoji"; +import {focusBlock} from "../../util/selection"; +import {resizeAV} from "../../util/resize"; export const avRender = (element: Element, protyle: IProtyle, cb?: () => void) => { let avElements: Element[] = []; @@ -21,7 +23,30 @@ export const avRender = (element: Element, protyle: IProtyle, cb?: () => void) = if (e.getAttribute("data-render") === "true") { return; } + let time: number; + if (e.firstElementChild.innerHTML === "") { + e.style.alignSelf = ""; + time = new Date().getTime(); + let html = ""; + [1, 2, 3].forEach(() => { + html += `
+
+
+
+
+
+
`; + }); + e.firstElementChild.innerHTML = html; + } const left = e.querySelector(".av__scroll")?.scrollLeft || 0; + const headerTransform = (e.querySelector(".av__row--header") as HTMLElement)?.style.transform; + const footerTransform = (e.querySelector(".av__row--footer") as HTMLElement)?.style.transform; + let selectCellId = ""; + const selectCellElement = e.querySelector(".av__cell--select") as HTMLElement; + if (selectCellElement) { + selectCellId = selectCellElement.parentElement.dataset.id + Constants.ZWSP + selectCellElement.getAttribute("data-col-id"); + } fetchPost("/api/av/renderAttributeView", { id: e.getAttribute("data-av-id"), }, (response) => { @@ -75,9 +100,6 @@ style="width: ${column.width || "200px"}">${getCalcValue(column) || '${urlContent}
`; - if (cell.value && cell.value[cell.valueType as "url"].content) { - text += ``; - } } else if (cell.valueType === "block") { text = `${cell.value.block.content || ""}`; if (cell.value?.isDetached) { @@ -89,7 +111,7 @@ style="width: ${column.width || "200px"}">${getCalcValue(column) || '${cell.value?.number.formattedContent || ""}`; } else if (cell.valueType === "mSelect" || cell.valueType === "select") { cell.value?.mSelect?.forEach((item) => { - text += `${item.content}`; + text += `${item.content}`; }); if (!text) { text = ''; @@ -97,17 +119,17 @@ style="width: ${column.width || "200px"}">${getCalcValue(column) || '${text}`; } } else if (cell.valueType === "date") { - text = ''; + text = ''; const dataValue = cell.value ? cell.value.date : null; if (dataValue && dataValue.isNotEmpty) { text += dayjs(dataValue.content).format("YYYY-MM-DD HH:mm"); } if (dataValue && dataValue.hasEndDate && dataValue.isNotEmpty && dataValue.isNotEmpty2) { - text += `${dayjs(dataValue.content2).format("YYYY-MM-DD HH:mm")}`; + text += `${dayjs(dataValue.content2).format("YYYY-MM-DD HH:mm")}`; } text += ""; } else if (["created", "updated"].includes(cell.valueType)) { - text = ''; + text = ''; const dataValue = cell.value ? cell.value[cell.valueType as "date"] : null; if (dataValue && dataValue.isNotEmpty) { text += dayjs(dataValue.content).format("YYYY-MM-DD HH:mm"); @@ -118,7 +140,7 @@ style="width: ${column.width || "200px"}">${getCalcValue(column) || '`; } else { - text += `${item.name}`; + text += `${item.name}`; } }); if (!text) { @@ -127,6 +149,11 @@ style="width: ${column.width || "200px"}">${getCalcValue(column) || '${text}`; } } + if (["text", "template", "url", "email", "phone", "number", "date", "created", "updated"].includes(cell.valueType)) { + if (cell.value && cell.value[cell.valueType as "url"].content) { + text += ``; + } + } tableHTML += `
${text}
`; ${item.name}
`; }); - - e.firstElementChild.outerHTML = `
+ setTimeout(() => { + e.firstElementChild.outerHTML = `
${tabHTML} @@ -181,11 +208,28 @@ ${cell.color ? `color:${cell.color};` : ""}">${text}
`;
`; - e.setAttribute("data-render", "true"); - e.querySelector(".av__scroll").scrollLeft = left; - if (cb) { - cb(); - } + e.setAttribute("data-render", "true"); + resizeAV(e); + if (left) { + e.querySelector(".av__scroll").scrollLeft = left; + } + if (headerTransform) { + (e.querySelector(".av__row--header") as HTMLElement).style.transform = headerTransform; + } + if (footerTransform) { + (e.querySelector(".av__row--footer") as HTMLElement).style.transform = footerTransform; + } + if (selectCellId) { + const newCellElement = e.querySelector(`.av__row[data-id="${selectCellId.split(Constants.ZWSP)[0]}"] .av__cell[data-col-id="${selectCellId.split(Constants.ZWSP)[1]}"]`); + if (newCellElement) { + newCellElement.classList.add("av__cell--select"); + } + focusBlock(e) + } + if (cb) { + cb(); + } + }, time ? 256 - (new Date().getTime() - time) : 0); // 为了让动画更好看,需延时到 256ms }); }); } @@ -194,6 +238,17 @@ ${cell.color ? `color:${cell.color};` : ""}">${text}
`; let lastParentID: string; let lastElement: HTMLElement; export const refreshAV = (protyle: IProtyle, operation: IOperation) => { + if (operation.action === "setAttrViewName") { + Array.from(protyle.wysiwyg.element.querySelectorAll(`[data-av-id="${operation.id}"]`)).forEach((item: HTMLElement) => { + const titleElement = item.querySelector(".av__title") as HTMLElement; + if (!titleElement) { + return; + } + titleElement.textContent = operation.data; + titleElement.dataset.title = operation.data; + item.querySelector(".layout-tab-bar .item__text").textContent = operation.data; + }); + } if (lastParentID === operation.parentID && protyle.contentElement.isSameNode(lastElement)) { return; } @@ -210,15 +265,6 @@ export const refreshAV = (protyle: IProtyle, operation: IOperation) => { (rowItem.querySelector(`[data-col-id="${operation.id}"]`) as HTMLElement).style.width = operation.data; }); }); - } else if (operation.action === "setAttrViewName") { - Array.from(protyle.wysiwyg.element.querySelectorAll(`[data-av-id="${avId}"]`)).forEach((item: HTMLElement) => { - const titleElement = item.querySelector(".av__title") as HTMLElement; - if (!titleElement || titleElement.textContent.trim() === operation.data) { - return; - } - titleElement.textContent = operation.data; - titleElement.dataset.title = operation.data; - }); } else { Array.from(protyle.wysiwyg.element.querySelectorAll(`[data-av-id="${avId}"]`)).forEach((item: HTMLElement) => { item.removeAttribute("data-render"); diff --git a/app/src/protyle/render/av/row.ts b/app/src/protyle/render/av/row.ts new file mode 100644 index 00000000000..28f675ad991 --- /dev/null +++ b/app/src/protyle/render/av/row.ts @@ -0,0 +1,76 @@ +import {hasClosestBlock} from "../../util/hasClosest"; +import {focusBlock} from "../../util/selection"; + +export const selectRow = (checkElement: Element, type: "toggle" | "select" | "unselect" | "unselectAll") => { + const rowElement = checkElement.parentElement; + const useElement = checkElement.querySelector(".icon__check use"); + if (rowElement.classList.contains("av__row--header") || type === "unselectAll") { + if ("#iconCheck" === useElement.getAttribute("xlink:href")) { + rowElement.parentElement.querySelectorAll(".av__firstcol").forEach(item => { + item.querySelector(".icon__check use").setAttribute("xlink:href", "#iconUncheck"); + item.parentElement.classList.remove("av__row--select"); + }); + } else { + rowElement.parentElement.querySelectorAll(".av__firstcol").forEach(item => { + item.querySelector(".icon__check use").setAttribute("xlink:href", "#iconCheck"); + item.parentElement.classList.add("av__row--select"); + }); + } + } else { + if (type === "select" || (useElement.getAttribute("xlink:href") === "#iconUncheck" && type === "toggle")) { + checkElement.parentElement.classList.add("av__row--select"); + useElement.setAttribute("xlink:href", "#iconCheck"); + } else if (type === "unselect" || (useElement.getAttribute("xlink:href") === "#iconCheck" && type === "toggle")) { + checkElement.parentElement.classList.remove("av__row--select"); + useElement.setAttribute("xlink:href", "#iconUncheck"); + } + } + focusBlock(hasClosestBlock(rowElement) as HTMLElement); + updateHeader(rowElement); +} + +export const updateHeader = (rowElement: HTMLElement) => { + const blockElement = hasClosestBlock(rowElement); + if (!blockElement) { + return; + } + const selectCount = rowElement.parentElement.querySelectorAll(".av__row--select:not(.av__row--header)").length; + const diffCount = rowElement.parentElement.childElementCount - 3 - selectCount; + const headElement = rowElement.parentElement.firstElementChild; + const headUseElement = headElement.querySelector("use"); + const counterElement = blockElement.querySelector(".av__counter"); + const avHeadElement = blockElement.querySelector(".av__header") as HTMLElement; + if (diffCount === 0 && rowElement.parentElement.childElementCount - 3 !== 0) { + headElement.classList.add("av__row--select"); + headUseElement.setAttribute("xlink:href", "#iconCheck"); + } else if (diffCount === rowElement.parentElement.childElementCount - 3) { + headElement.classList.remove("av__row--select"); + headUseElement.setAttribute("xlink:href", "#iconUncheck"); + counterElement.classList.add("fn__none"); + avHeadElement.style.position = ""; + return; + } else if (diffCount > 0) { + headElement.classList.add("av__row--select"); + headUseElement.setAttribute("xlink:href", "#iconIndeterminateCheck"); + } + counterElement.classList.remove("fn__none"); + counterElement.innerHTML = `${selectCount} selected`; + avHeadElement.style.position = "sticky"; +}; + +export const insertAttrViewBlockAnimation = (blockElement: Element, size: number, previousId: string, avId?: string) => { + const previousElement = blockElement.querySelector(`.av__row[data-id="${previousId}"]`) || blockElement.querySelector(".av__row--header"); + let colHTML = ""; + previousElement.querySelectorAll(".av__cell").forEach((item: HTMLElement) => { + colHTML += `
`; + }); + + let html = ""; + new Array(size).fill(1).forEach(() => { + html += `
+
+ ${colHTML} +
`; + }); + previousElement.insertAdjacentHTML("afterend", html); +}; diff --git a/app/src/protyle/render/av/sort.ts b/app/src/protyle/render/av/sort.ts index 834b5a66c83..8dc14fc112a 100644 --- a/app/src/protyle/render/av/sort.ts +++ b/app/src/protyle/render/av/sort.ts @@ -24,7 +24,7 @@ export const addSort = (options: { if (!hasSort) { menu.addItem({ label: column.name, - iconHTML: `${column.icon ? unicode2Emoji(column.icon) : ``}`, + iconHTML: column.icon ? unicode2Emoji(column.icon, "b3-menu__icon", true) : ``, click: () => { const oldSorts = Object.assign([], options.data.view.sorts); options.data.view.sorts.push({ diff --git a/app/src/protyle/toolbar/index.ts b/app/src/protyle/toolbar/index.ts index 30a41e087fe..1573047559c 100644 --- a/app/src/protyle/toolbar/index.ts +++ b/app/src/protyle/toolbar/index.ts @@ -832,7 +832,7 @@ export class Toolbar { -`; +`; const autoHeight = () => { textElement.style.height = textElement.scrollHeight + "px"; if (isMobile()) { diff --git a/app/src/protyle/util/editorCommonEvent.ts b/app/src/protyle/util/editorCommonEvent.ts index 9dbe5672740..b2e6d950090 100644 --- a/app/src/protyle/util/editorCommonEvent.ts +++ b/app/src/protyle/util/editorCommonEvent.ts @@ -19,7 +19,7 @@ import {uploadLocalFiles} from "../upload"; import {insertHTML} from "./insertHTML"; import {isBrowser} from "../../util/functions"; import {hideElements} from "../ui/hideElements"; -import {insertAttrViewBlockAnimation} from "../render/av/action"; +import {insertAttrViewBlockAnimation} from "../render/av/row"; const moveToNew = (protyle: IProtyle, sourceElements: Element[], targetElement: Element, newSourceElement: Element, isSameDoc: boolean, isBottom: boolean, isCopy: boolean) => { diff --git a/app/src/protyle/util/resize.ts b/app/src/protyle/util/resize.ts index 6dfe2e24954..2291a9fe14c 100644 --- a/app/src/protyle/util/resize.ts +++ b/app/src/protyle/util/resize.ts @@ -44,3 +44,36 @@ export const resize = (protyle: IProtyle) => { } }, Constants.TIMEOUT_TRANSITION); // 等待 setPadding 动画结束 }; + +export const resizeAV = (item: HTMLElement) => { + if (!item.classList.contains("av") || item.getAttribute("data-render") !== "true") { + return + } + if (item.style.width.endsWith("%") || item.style.margin) { + const avHeaderElement = item.firstElementChild.firstElementChild as HTMLElement; + avHeaderElement.style.paddingLeft = ""; + avHeaderElement.style.paddingRight = ""; + const avBodyElement = item.querySelector(".av__scroll").firstElementChild as HTMLElement; + avBodyElement.style.paddingLeft = ""; + avBodyElement.style.paddingRight = ""; + item.style.alignSelf = ""; + if (!item.style.width.endsWith("%")) { + item.style.width = "" + item.style.maxWidth = "100%"; + } + } else { + const paddingLeft = item.parentElement.style.paddingLeft; + const paddingRight = item.parentElement.style.paddingRight; + const avHeaderElement = item.firstElementChild.firstElementChild as HTMLElement; + avHeaderElement.style.paddingLeft = paddingLeft; + avHeaderElement.style.paddingRight = paddingRight; + const avBodyElement = item.querySelector(".av__scroll").firstElementChild as HTMLElement; + avBodyElement.style.paddingLeft = paddingLeft; + avBodyElement.style.paddingRight = paddingRight; + item.style.alignSelf = "center"; + if (item.parentElement.clientWidth > 0) { + item.style.width = item.parentElement.clientWidth + "px"; + item.style.maxWidth = ""; + } + } +} diff --git a/app/src/protyle/wysiwyg/index.ts b/app/src/protyle/wysiwyg/index.ts index d3e2967b933..bf9570e1d79 100644 --- a/app/src/protyle/wysiwyg/index.ts +++ b/app/src/protyle/wysiwyg/index.ts @@ -76,6 +76,7 @@ import {removeSearchMark} from "../toolbar/util"; import {activeBlur, hideKeyboardToolbar} from "../../mobile/util/keyboardToolbar"; import {commonClick} from "./commonClick"; import {avClick, avContextmenu, updateAVName} from "../render/av/action"; +import {updateHeader} from "../render/av/row"; export class WYSIWYG { public lastHTMLs: { [key: string]: string } = {}; @@ -185,10 +186,16 @@ export class WYSIWYG { private setEmptyOutline(protyle: IProtyle, element: HTMLElement) { // 图片移除选择状态应放在前面,否则 https://github.com/siyuan-note/siyuan/issues/4173 - const selectImgElement = protyle.wysiwyg.element.querySelector(".img--select"); - if (selectImgElement) { - selectImgElement.classList.remove("img--select"); - } + protyle.wysiwyg.element.querySelectorAll(".img--select, .av__cell--select, .av__row--select").forEach((item: HTMLElement) => { + if (item.classList.contains("av__row--select") && !hasClosestByClassName(element, "av")) { + item.classList.remove("av__row--select"); + item.querySelector(".av__firstcol use").setAttribute("xlink:href", "#iconUncheck"); + updateHeader(item); + } else { + item.classList.remove("img--select", "av__cell--select"); + } + }); + let nodeElement = element; if (!element.getAttribute("data-node-id")) { const tempElement = hasClosestBlock(element); @@ -1334,7 +1341,14 @@ export class WYSIWYG { } const nodeElement = hasClosestBlock(target); - if (avContextmenu(protyle, event, target)) { + const avRowElement = hasClosestByClassName(target, "av__row") + if (avRowElement && avContextmenu(protyle, avRowElement, { + x: event.clientX, + y: avRowElement.getBoundingClientRect().bottom, + h: avRowElement.clientHeight + })) { + event.stopPropagation(); + event.preventDefault(); return; } if (!nodeElement) { diff --git a/app/src/protyle/wysiwyg/keydown.ts b/app/src/protyle/wysiwyg/keydown.ts index 6a052db01c0..e592b992228 100644 --- a/app/src/protyle/wysiwyg/keydown.ts +++ b/app/src/protyle/wysiwyg/keydown.ts @@ -69,6 +69,8 @@ import {escapeHtml} from "../../util/escape"; import {insertHTML} from "../util/insertHTML"; import {removeSearchMark} from "../toolbar/util"; import {copyPNG} from "../../menus/util"; +import {avKeydown} from "../render/av/keydown"; +import {resizeAV} from "../util/resize"; const getContentByInlineHTML = (range: Range, cb: (content: string) => void) => { @@ -118,12 +120,10 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => { return; } - if (nodeElement.classList.contains("av")) { - if (matchHotKey("⌘B", event) || matchHotKey("⌘I", event) || matchHotKey("⌘U", event)) { - event.preventDefault(); - } + if (avKeydown(event, nodeElement, protyle)) { return; } + if (nodeElement.classList.contains("protyle-wysiwyg--select") && !isCtrl(event) && !event.shiftKey && !event.altKey) { if (event.key.toLowerCase() === "a") { event.stopPropagation(); @@ -634,7 +634,7 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => { ) || (!firstEditElement && nodeElement.isSameNode(protyle.wysiwyg.element.firstElementChild))) { // 不能用\n判断,否则文字过长折行将错误 https://github.com/siyuan-note/siyuan/issues/6156 - if (getSelectionPosition(nodeElement, range).top - protyle.wysiwyg.element.getBoundingClientRect().top < 40) { + if (getSelectionPosition(nodeElement, range).top - protyle.wysiwyg.element.getBoundingClientRect().top < 40 || nodeElement.classList.contains("av")) { if (protyle.title && (protyle.wysiwyg.element.firstElementChild.getAttribute("data-eof") === "1" || protyle.contentElement.scrollTop === 0)) { protyle.title.editElement.focus(); @@ -1175,7 +1175,12 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => { selectElements = [nodeElement]; } updateBatchTransaction(selectElements, protyle, (e: HTMLElement) => { - e.style.textAlign = "left"; + if (e.classList.contains("av")) { + e.style.margin = "" + resizeAV(e); + } else { + e.style.textAlign = "left"; + } }); } event.stopPropagation(); @@ -1192,7 +1197,12 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => { selectElements = [nodeElement]; } updateBatchTransaction(selectElements, protyle, (e: HTMLElement) => { - e.style.textAlign = "center"; + if (e.classList.contains("av")) { + e.style.margin = "0 auto" + resizeAV(e); + } else { + e.style.textAlign = "center"; + } }); } event.stopPropagation(); @@ -1205,7 +1215,12 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => { selectElements = [nodeElement]; } updateBatchTransaction(selectElements, protyle, (e: HTMLElement) => { - e.style.textAlign = "right"; + if (e.classList.contains("av")) { + e.style.margin = "0 0 0 auto"; + resizeAV(e); + } else { + e.style.textAlign = "right"; + } }); event.stopPropagation(); event.preventDefault(); diff --git a/app/src/protyle/wysiwyg/transaction.ts b/app/src/protyle/wysiwyg/transaction.ts index 9d2b12043e2..886d09d375c 100644 --- a/app/src/protyle/wysiwyg/transaction.ts +++ b/app/src/protyle/wysiwyg/transaction.ts @@ -713,6 +713,10 @@ export const onTransaction = (protyle: IProtyle, operation: IOperation, isUndo: "setAttrViewSorts", "setAttrViewColCalc", "removeAttrViewCol", "updateAttrViewColNumberFormat", "removeAttrViewBlock", "replaceAttrViewBlock", "updateAttrViewColTemplate", "setAttrViewColIcon"].includes(operation.action)) { refreshAV(protyle, operation); + } else if (operation.action === "doUpdateUpdated") { + updateElements.forEach(item => { + item.setAttribute("updated", operation.data); + }) } }; diff --git a/app/src/types/index.d.ts b/app/src/types/index.d.ts index 54b09d781d1..99c24586676 100644 --- a/app/src/types/index.d.ts +++ b/app/src/types/index.d.ts @@ -34,6 +34,7 @@ type TOperation = | "removeAttrViewColOption" | "updateAttrViewColOption" | "setAttrViewName" + | "doUpdateUpdated" | "setAttrViewColIcon" | "setAttrViewFilters" | "setAttrViewSorts" diff --git a/kernel/api/transaction.go b/kernel/api/transaction.go index ba6dbc2a394..ff4a61f8e8f 100644 --- a/kernel/api/transaction.go +++ b/kernel/api/transaction.go @@ -83,8 +83,10 @@ func pushTransactions(app, session string, transactions []*model.Transaction) { pushMode := util.PushModeBroadcastExcludeSelf if 0 < len(transactions) && 0 < len(transactions[0].DoOperations) { model.WaitForWritingFiles() - if strings.Contains(strings.ToLower(transactions[0].DoOperations[0].Action), "attrview") { - pushMode = util.PushModeBroadcast + if action := transactions[0].DoOperations[0].Action; strings.Contains(strings.ToLower(action), "attrview") { + if "setAttrViewName" != action { + pushMode = util.PushModeBroadcast + } } } diff --git a/kernel/av/av.go b/kernel/av/av.go index 9c7c708a8a8..e172c4d32f8 100644 --- a/kernel/av/av.go +++ b/kernel/av/av.go @@ -130,6 +130,8 @@ func (value *Value) String() string { return value.Number.FormattedContent case KeyTypeDate: return value.Date.FormattedContent + case KeyTypeSelect: + return value.MSelect[0].Content case KeyTypeMSelect: var ret []string for _, v := range value.MSelect { @@ -520,6 +522,14 @@ func SaveAttributeView(av *AttributeView) (err error) { // 补全 block 的创建时间和更新时间 for _, v := range kv.Values { if 0 == v.Block.Created { + if "" == v.Block.ID { + v.Block.ID = v.BlockID + if "" == v.Block.ID { + v.Block.ID = ast.NewNodeID() + v.BlockID = v.Block.ID + } + } + createdStr := v.Block.ID[:len("20060102150405")] created, parseErr := time.ParseInLocation("20060102150405", createdStr, time.Local) if nil == parseErr { diff --git a/kernel/av/table.go b/kernel/av/table.go index a829dec2a7a..90153d1b47c 100644 --- a/kernel/av/table.go +++ b/kernel/av/table.go @@ -19,6 +19,7 @@ package av import ( "math" "sort" + "strconv" "strings" ) @@ -72,14 +73,6 @@ const ( CalcOperatorLatest CalcOperator = "Latest" ) -type TableCell struct { - ID string `json:"id"` - Value *Value `json:"value"` - ValueType KeyType `json:"valueType"` - Color string `json:"color"` - BgColor string `json:"bgColor"` -} - func (value *Value) Compare(other *Value) int { if nil == value { return -1 @@ -161,6 +154,9 @@ func (value *Value) Compare(other *Value) int { } return strings.Compare(v1, v2) } + if nil != value.Template && nil != other.Template { + return strings.Compare(value.Template.Content, other.Template.Content) + } return 0 } @@ -473,6 +469,65 @@ func (value *Value) CompareOperator(other *Value, operator FilterOperator) bool return 0 != len(value.MAsset) && !(1 == len(value.MAsset) && "" == value.MAsset[0].Content) } } + + if nil != value.Template && nil != other.Template { + switch operator { + case FilterOperatorIsEqual: + if "" == strings.TrimSpace(other.Template.Content) { + return true + } + return value.Template.Content == other.Template.Content + case FilterOperatorIsNotEqual: + if "" == strings.TrimSpace(other.Template.Content) { + return true + } + return value.Template.Content != other.Template.Content + case FilterOperatorIsGreater: + if "" == strings.TrimSpace(other.Template.Content) { + return true + } + return value.Template.Content > other.Template.Content + case FilterOperatorIsGreaterOrEqual: + if "" == strings.TrimSpace(other.Template.Content) { + return true + } + return value.Template.Content >= other.Template.Content + case FilterOperatorIsLess: + if "" == strings.TrimSpace(other.Template.Content) { + return true + } + return value.Template.Content < other.Template.Content + case FilterOperatorIsLessOrEqual: + if "" == strings.TrimSpace(other.Template.Content) { + return true + } + return value.Template.Content <= other.Template.Content + case FilterOperatorContains: + if "" == strings.TrimSpace(other.Template.Content) { + return true + } + return strings.Contains(value.Template.Content, other.Template.Content) + case FilterOperatorDoesNotContain: + if "" == strings.TrimSpace(other.Template.Content) { + return true + } + return !strings.Contains(value.Template.Content, other.Template.Content) + case FilterOperatorStartsWith: + if "" == strings.TrimSpace(other.Template.Content) { + return true + } + return strings.HasPrefix(value.Template.Content, other.Template.Content) + case FilterOperatorEndsWith: + if "" == strings.TrimSpace(other.Template.Content) { + return true + } + return strings.HasSuffix(value.Template.Content, other.Template.Content) + case FilterOperatorIsEmpty: + return "" == strings.TrimSpace(value.Template.Content) + case FilterOperatorIsNotEmpty: + return "" != strings.TrimSpace(value.Template.Content) + } + } return true } @@ -503,6 +558,14 @@ type TableColumn struct { Template string `json:"template"` // 模板内容 } +type TableCell struct { + ID string `json:"id"` + Value *Value `json:"value"` + ValueType KeyType `json:"valueType"` + Color string `json:"color"` + BgColor string `json:"bgColor"` +} + type TableRow struct { ID string `json:"id"` Cells []*TableCell `json:"cells"` @@ -714,6 +777,87 @@ func (table *Table) calcColTemplate(col *TableColumn, colIndex int) { if 0 < len(table.Rows) { col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(table.Rows)), NumberFormatPercent)} } + case CalcOperatorSum: + sum := 0.0 + for _, row := range table.Rows { + if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Template && "" != row.Cells[colIndex].Value.Template.Content { + val, _ := strconv.ParseFloat(row.Cells[colIndex].Value.Template.Content, 64) + sum += val + } + } + col.Calc.Result = &Value{Number: NewFormattedValueNumber(sum, col.NumberFormat)} + case CalcOperatorAverage: + sum := 0.0 + count := 0 + for _, row := range table.Rows { + if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Template && "" != row.Cells[colIndex].Value.Template.Content { + val, _ := strconv.ParseFloat(row.Cells[colIndex].Value.Template.Content, 64) + sum += val + count++ + } + } + if 0 != count { + col.Calc.Result = &Value{Number: NewFormattedValueNumber(sum/float64(count), col.NumberFormat)} + } + case CalcOperatorMedian: + values := []float64{} + for _, row := range table.Rows { + if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Template && "" != row.Cells[colIndex].Value.Template.Content { + val, _ := strconv.ParseFloat(row.Cells[colIndex].Value.Template.Content, 64) + values = append(values, val) + } + } + sort.Float64s(values) + if len(values) > 0 { + if len(values)%2 == 0 { + col.Calc.Result = &Value{Number: NewFormattedValueNumber((values[len(values)/2-1]+values[len(values)/2])/2, col.NumberFormat)} + } else { + col.Calc.Result = &Value{Number: NewFormattedValueNumber(values[len(values)/2], col.NumberFormat)} + } + } + case CalcOperatorMin: + minVal := math.MaxFloat64 + for _, row := range table.Rows { + if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Template && "" != row.Cells[colIndex].Value.Template.Content { + val, _ := strconv.ParseFloat(row.Cells[colIndex].Value.Template.Content, 64) + if val < minVal { + minVal = val + } + } + } + if math.MaxFloat64 != minVal { + col.Calc.Result = &Value{Number: NewFormattedValueNumber(minVal, col.NumberFormat)} + } + case CalcOperatorMax: + maxVal := -math.MaxFloat64 + for _, row := range table.Rows { + if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Template && "" != row.Cells[colIndex].Value.Template.Content { + val, _ := strconv.ParseFloat(row.Cells[colIndex].Value.Template.Content, 64) + if val > maxVal { + maxVal = val + } + } + } + if -math.MaxFloat64 != maxVal { + col.Calc.Result = &Value{Number: NewFormattedValueNumber(maxVal, col.NumberFormat)} + } + case CalcOperatorRange: + minVal := math.MaxFloat64 + maxVal := -math.MaxFloat64 + for _, row := range table.Rows { + if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Template && "" != row.Cells[colIndex].Value.Template.Content { + val, _ := strconv.ParseFloat(row.Cells[colIndex].Value.Template.Content, 64) + if val < minVal { + minVal = val + } + if val > maxVal { + maxVal = val + } + } + } + if math.MaxFloat64 != minVal && -math.MaxFloat64 != maxVal { + col.Calc.Result = &Value{Number: NewFormattedValueNumber(maxVal-minVal, col.NumberFormat)} + } } } diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go index 0f14c3c2211..038f081efe1 100644 --- a/kernel/model/attribute_view.go +++ b/kernel/model/attribute_view.go @@ -39,66 +39,6 @@ type BlockAttributeViewKeys struct { KeyValues []*av.KeyValues `json:"keyValues"` } -func renderTemplateCol(ial map[string]string, tplContent string, rowValues []*av.KeyValues) string { - if "" == ial["id"] { - block := getRowBlockValue(rowValues) - ial["id"] = block.Block.ID - } - if "" == ial["updated"] { - block := getRowBlockValue(rowValues) - ial["updated"] = time.UnixMilli(block.Block.Updated).Format("20060102150405") - } - - funcMap := sprig.TxtFuncMap() - goTpl := template.New("").Delims(".action{", "}") - tplContent = strings.ReplaceAll(tplContent, ".custom-", ".custom_") // 模板中的属性名不允许包含 - 字符,因此这里需要替换 - tpl, tplErr := goTpl.Funcs(funcMap).Parse(tplContent) - if nil != tplErr { - logging.LogWarnf("parse template [%s] failed: %s", tplContent, tplErr) - return "" - } - - buf := &bytes.Buffer{} - dataModel := map[string]interface{}{} // 复制一份 IAL 以避免修改原始数据 - for k, v := range ial { - dataModel[strings.ReplaceAll(k, "custom-", "custom_")] = v - - // Database template column supports `created` and `updated` built-in variables https://github.com/siyuan-note/siyuan/issues/9364 - createdStr := ial["id"] - if "" != createdStr { - createdStr = createdStr[:len("20060102150405")] - } - created, parseErr := time.ParseInLocation("20060102150405", createdStr, time.Local) - if nil == parseErr { - dataModel["created"] = created - } else { - logging.LogWarnf("parse created [%s] failed: %s", createdStr, parseErr) - dataModel["created"] = time.Now() - } - updatedStr := ial["updated"] - updated, parseErr := time.ParseInLocation("20060102150405", updatedStr, time.Local) - if nil == parseErr { - dataModel["updated"] = updated - } else { - dataModel["updated"] = time.Now() - } - } - for _, rowValue := range rowValues { - if 0 < len(rowValue.Values) { - v := rowValue.Values[0] - if av.KeyTypeNumber == v.Type { - dataModel[rowValue.Key.Name] = v.Number.Content - } else { - dataModel[rowValue.Key.Name] = v.String() - } - } - } - if err := tpl.Execute(buf, dataModel); nil != err { - logging.LogWarnf("execute template [%s] failed: %s", tplContent, err) - } - return buf.String() -} - func GetBlockAttributeViewKeys(blockID string) (ret []*BlockAttributeViewKeys) { waitForSyncingStorages() @@ -124,10 +64,6 @@ func GetBlockAttributeViewKeys(blockID string) (ret []*BlockAttributeViewKeys) { var keyValues []*av.KeyValues for _, kv := range attrView.KeyValues { - if av.KeyTypeBlock == kv.Key.Type { - continue - } - kValues := &av.KeyValues{Key: kv.Key} for _, v := range kv.Values { if v.BlockID == blockID { @@ -181,7 +117,11 @@ func GetBlockAttributeViewKeys(blockID string) (ret []*BlockAttributeViewKeys) { switch kv.Key.Type { case av.KeyTypeTemplate: if 0 < len(kv.Values) { - ial := GetBlockAttrs(blockID) + ial := map[string]string{} + block := getRowBlockValue(keyValues) + if !block.IsDetached { + ial = GetBlockAttrs(blockID) + } kv.Values[0].Template.Content = renderTemplateCol(ial, kv.Key.Template, keyValues) } } @@ -244,22 +184,30 @@ func RenderAttributeView(avID string) (viewable av.Viewable, attrView *av.Attrib } // 做一些数据兼容处理,保存的时候也会做 av.SaveAttributeView() - now := util.CurrentTimeMillis() + currentTimeMillis := util.CurrentTimeMillis() for _, kv := range attrView.KeyValues { switch kv.Key.Type { case av.KeyTypeBlock: // 补全 block 的创建时间和更新时间 for _, v := range kv.Values { if 0 == v.Block.Created { + if "" == v.Block.ID { + v.Block.ID = v.BlockID + if "" == v.Block.ID { + v.Block.ID = ast.NewNodeID() + v.BlockID = v.Block.ID + } + } + createdStr := v.Block.ID[:len("20060102150405")] created, parseErr := time.ParseInLocation("20060102150405", createdStr, time.Local) if nil == parseErr { v.Block.Created = created.UnixMilli() } else { - v.Block.Created = now + v.Block.Created = currentTimeMillis } } if 0 == v.Block.Updated { - v.Block.Updated = now + v.Block.Updated = currentTimeMillis } } } @@ -293,6 +241,65 @@ func RenderAttributeView(avID string) (viewable av.Viewable, attrView *av.Attrib return } +func renderTemplateCol(ial map[string]string, tplContent string, rowValues []*av.KeyValues) string { + if "" == ial["id"] { + block := getRowBlockValue(rowValues) + ial["id"] = block.Block.ID + } + if "" == ial["updated"] { + block := getRowBlockValue(rowValues) + ial["updated"] = time.UnixMilli(block.Block.Updated).Format("20060102150405") + } + + funcMap := sprig.TxtFuncMap() + goTpl := template.New("").Delims(".action{", "}") + tpl, tplErr := goTpl.Funcs(funcMap).Parse(tplContent) + if nil != tplErr { + logging.LogWarnf("parse template [%s] failed: %s", tplContent, tplErr) + return "" + } + + buf := &bytes.Buffer{} + dataModel := map[string]interface{}{} // 复制一份 IAL 以避免修改原始数据 + for k, v := range ial { + dataModel[k] = v + + // Database template column supports `created` and `updated` built-in variables https://github.com/siyuan-note/siyuan/issues/9364 + createdStr := ial["id"] + if "" != createdStr { + createdStr = createdStr[:len("20060102150405")] + } + created, parseErr := time.ParseInLocation("20060102150405", createdStr, time.Local) + if nil == parseErr { + dataModel["created"] = created + } else { + logging.LogWarnf("parse created [%s] failed: %s", createdStr, parseErr) + dataModel["created"] = time.Now() + } + updatedStr := ial["updated"] + updated, parseErr := time.ParseInLocation("20060102150405", updatedStr, time.Local) + if nil == parseErr { + dataModel["updated"] = updated + } else { + dataModel["updated"] = time.Now() + } + } + for _, rowValue := range rowValues { + if 0 < len(rowValue.Values) { + v := rowValue.Values[0] + if av.KeyTypeNumber == v.Type { + dataModel[rowValue.Key.Name] = v.Number.Content + } else { + dataModel[rowValue.Key.Name] = v.String() + } + } + } + if err := tpl.Execute(buf, dataModel); nil != err { + logging.LogWarnf("execute template [%s] failed: %s", tplContent, err) + } + return buf.String() +} + func renderAttributeViewTable(attrView *av.AttributeView, view *av.View) (ret *av.Table, err error) { ret = &av.Table{ ID: view.ID, @@ -414,7 +421,11 @@ func renderAttributeViewTable(attrView *av.AttributeView, view *av.View) (ret *a switch cell.ValueType { case av.KeyTypeTemplate: // 渲染模板列 keyValues := rows[row.ID] - ial := GetBlockAttrs(row.ID) + ial := map[string]string{} + block := row.GetBlockValue() + if !block.IsDetached { + ial = GetBlockAttrs(row.ID) + } content := renderTemplateCol(ial, cell.Value.Template.Content, keyValues) cell.Value.Template.Content = content case av.KeyTypeCreated: // 渲染创建时间 @@ -427,7 +438,11 @@ func renderAttributeViewTable(attrView *av.AttributeView, view *av.View) (ret *a cell.Value.Created = av.NewFormattedValueCreated(time.Now().UnixMilli(), 0, av.CreatedFormatNone) } case av.KeyTypeUpdated: // 渲染更新时间 - ial := GetBlockAttrs(row.ID) + ial := map[string]string{} + block := row.GetBlockValue() + if !block.IsDetached { + ial = GetBlockAttrs(row.ID) + } updatedStr := ial["updated"] if "" == updatedStr { block := row.GetBlockValue() diff --git a/kernel/model/blockinfo.go b/kernel/model/blockinfo.go index 7f3163b4aab..c3e91703dc5 100644 --- a/kernel/model/blockinfo.go +++ b/kernel/model/blockinfo.go @@ -304,6 +304,8 @@ func buildBlockBreadcrumb(node *ast.Node, excludeTypes []string) (ret []*BlockPa name := util.EscapeHTML(parent.IALAttr("name")) if ast.NodeDocument == parent.Type { name = util.EscapeHTML(box.Name) + util.EscapeHTML(hPath) + } else if ast.NodeAttributeView == parent.Type { + name = treenode.GetAttributeViewName(parent.AttributeViewID) } else { if "" == name { if ast.NodeListItem == parent.Type { diff --git a/kernel/model/plugin.go b/kernel/model/plugin.go index 1bdccc70978..f873b7316f3 100644 --- a/kernel/model/plugin.go +++ b/kernel/model/plugin.go @@ -74,7 +74,7 @@ func SetPetalEnabled(name string, enabled bool, frontend string) (ret *Petal, er func LoadPetals(frontend string) (ret []*Petal) { ret = []*Petal{} - if Conf.Bazaar.PetalDisabled { + if Conf.Bazaar.PetalDisabled || !Conf.Bazaar.Trust { return } diff --git a/kernel/model/transaction.go b/kernel/model/transaction.go index 67eef3fc397..24608eacc5d 100644 --- a/kernel/model/transaction.go +++ b/kernel/model/transaction.go @@ -190,6 +190,8 @@ func performTx(tx *Transaction) (ret *TxErr) { ret = tx.doUnfoldHeading(op) case "setAttrs": ret = tx.doSetAttrs(op) + case "doUpdateUpdated": + ret = tx.doUpdateUpdated(op) case "addFlashcards": ret = tx.doAddFlashcards(op) case "removeFlashcards": @@ -987,6 +989,34 @@ func (tx *Transaction) doUpdate(operation *Operation) (ret *TxErr) { return } +func (tx *Transaction) doUpdateUpdated(operation *Operation) (ret *TxErr) { + id := operation.ID + tree, err := tx.loadTree(id) + if nil != err { + if errors.Is(err, ErrBlockNotFound) { + logging.LogWarnf("not found block [%s]", id) + return + } + + logging.LogErrorf("load tree [%s] failed: %s", id, err) + return &TxErr{code: TxErrCodeBlockNotFound, id: id} + } + + node := treenode.GetNodeInTree(tree, id) + if nil == node { + logging.LogErrorf("get node [%s] in tree [%s] failed", id, tree.Root.ID) + return &TxErr{msg: ErrBlockNotFound.Error(), id: id} + } + + node.SetIALAttr("updated", operation.Data.(string)) + createdUpdated(node) + tx.nodes[node.ID] = node + if err = tx.writeTree(tree); nil != err { + return &TxErr{code: TxErrCodeWriteTree, msg: err.Error(), id: id} + } + return +} + func (tx *Transaction) doCreate(operation *Operation) (ret *TxErr) { tree := operation.Data.(*parse.Tree) tx.writeTree(tree) diff --git a/kernel/treenode/node.go b/kernel/treenode/node.go index 90211fe0f38..0b7f3e3dab4 100644 --- a/kernel/treenode/node.go +++ b/kernel/treenode/node.go @@ -18,9 +18,13 @@ package treenode import ( "bytes" + "github.com/Masterminds/sprig/v3" "github.com/siyuan-note/siyuan/kernel/av" + "github.com/siyuan-note/siyuan/kernel/cache" "strings" "sync" + "text/template" + "time" "github.com/88250/gulu" "github.com/88250/lute" @@ -140,19 +144,10 @@ func NodeStaticContent(node *ast.Node, excludeTypes []string, includeTextMarkATi if ast.NodeDocument == node.Type { return node.IALAttr("title") - } else if ast.NodeAttributeView == node.Type { - if "" != node.AttributeViewID { - attrView, err := av.ParseAttributeView(node.AttributeViewID) - if nil == err { - buf := bytes.Buffer{} - for _, v := range attrView.Views { - buf.WriteString(v.Name) - buf.WriteString(" ") - } - return strings.TrimSpace(buf.String()) - } - } - return "" + } + + if ast.NodeAttributeView == node.Type { + return getAttributeViewContent(node.AttributeViewID) } buf := bytes.Buffer{} @@ -165,7 +160,7 @@ func NodeStaticContent(node *ast.Node, excludeTypes []string, includeTextMarkATi if n.IsContainerBlock() { if !lastSpace { - buf.WriteString(" ") + buf.WriteByte(' ') lastSpace = true } return ast.WalkContinue @@ -479,3 +474,319 @@ func IsChartCodeBlockCode(code *ast.Node) bool { language = strings.ReplaceAll(language, editor.Caret, "") return render.NoHighlight(language) } + +func GetAttributeViewName(avID string) (name string) { + if "" == avID { + return + } + + attrView, err := av.ParseAttributeView(avID) + if nil != err { + logging.LogErrorf("parse attribute view [%s] failed: %s", avID, err) + return + } + + buf := bytes.Buffer{} + for _, v := range attrView.Views { + buf.WriteString(v.Name) + buf.WriteByte(' ') + } + + name = strings.TrimSpace(buf.String()) + return +} + +func getAttributeViewContent(avID string) (content string) { + if "" == avID { + return + } + + attrView, err := av.ParseAttributeView(avID) + if nil != err { + logging.LogErrorf("parse attribute view [%s] failed: %s", avID, err) + return + } + + buf := bytes.Buffer{} + for _, v := range attrView.Views { + buf.WriteString(v.Name) + buf.WriteByte(' ') + } + + if 1 > len(attrView.Views) { + content = strings.TrimSpace(buf.String()) + return + } + + var view *av.View + for _, v := range attrView.Views { + if av.LayoutTypeTable == v.LayoutType { + view = v + break + } + } + if nil == view { + content = buf.String() + return + } + + table, err := renderAttributeViewTable(attrView, view) + if nil != err { + content = strings.TrimSpace(buf.String()) + return + } + + for _, col := range table.Columns { + buf.WriteString(col.Name) + buf.WriteByte(' ') + } + + for _, row := range table.Rows { + for _, cell := range row.Cells { + if nil == cell.Value { + continue + } + buf.WriteString(cell.Value.String()) + buf.WriteByte(' ') + } + } + + content = strings.TrimSpace(buf.String()) + return +} + +func renderAttributeViewTable(attrView *av.AttributeView, view *av.View) (ret *av.Table, err error) { + ret = &av.Table{ + ID: view.ID, + Name: view.Name, + Columns: []*av.TableColumn{}, + Rows: []*av.TableRow{}, + } + + // 组装列 + for _, col := range view.Table.Columns { + key, getErr := attrView.GetKey(col.ID) + if nil != getErr { + err = getErr + return + } + + ret.Columns = append(ret.Columns, &av.TableColumn{ + ID: key.ID, + Name: key.Name, + Type: key.Type, + Icon: key.Icon, + Options: key.Options, + NumberFormat: key.NumberFormat, + Template: key.Template, + Wrap: col.Wrap, + Hidden: col.Hidden, + Width: col.Width, + Calc: col.Calc, + }) + } + + // 生成行 + rows := map[string][]*av.KeyValues{} + for _, keyValues := range attrView.KeyValues { + for _, val := range keyValues.Values { + values := rows[val.BlockID] + if nil == values { + values = []*av.KeyValues{{Key: keyValues.Key, Values: []*av.Value{val}}} + } else { + values = append(values, &av.KeyValues{Key: keyValues.Key, Values: []*av.Value{val}}) + } + rows[val.BlockID] = values + } + } + + // 过滤掉不存在的行 + var notFound []string + for blockID, keyValues := range rows { + blockValue := getRowBlockValue(keyValues) + if nil == blockValue { + notFound = append(notFound, blockID) + continue + } + + if blockValue.IsDetached { + continue + } + + if nil != blockValue.Block && "" == blockValue.Block.ID { + notFound = append(notFound, blockID) + continue + } + + if GetBlockTree(blockID) == nil { + notFound = append(notFound, blockID) + } + } + for _, blockID := range notFound { + delete(rows, blockID) + } + + // 生成行单元格 + for rowID, row := range rows { + var tableRow av.TableRow + for _, col := range ret.Columns { + var tableCell *av.TableCell + for _, keyValues := range row { + if keyValues.Key.ID == col.ID { + tableCell = &av.TableCell{ + ID: keyValues.Values[0].ID, + Value: keyValues.Values[0], + ValueType: col.Type, + } + break + } + } + if nil == tableCell { + tableCell = &av.TableCell{ + ID: ast.NewNodeID(), + ValueType: col.Type, + } + } + tableRow.ID = rowID + + switch tableCell.ValueType { + case av.KeyTypeNumber: // 格式化数字 + if nil != tableCell.Value && nil != tableCell.Value.Number && tableCell.Value.Number.IsNotEmpty { + tableCell.Value.Number.Format = col.NumberFormat + tableCell.Value.Number.FormatNumber() + } + case av.KeyTypeTemplate: // 渲染模板列 + tableCell.Value = &av.Value{ID: tableCell.ID, KeyID: col.ID, BlockID: rowID, Type: av.KeyTypeTemplate, Template: &av.ValueTemplate{Content: col.Template}} + case av.KeyTypeCreated: // 填充创建时间列值,后面再渲染 + tableCell.Value = &av.Value{ID: tableCell.ID, KeyID: col.ID, BlockID: rowID, Type: av.KeyTypeCreated} + case av.KeyTypeUpdated: // 填充更新时间列值,后面再渲染 + tableCell.Value = &av.Value{ID: tableCell.ID, KeyID: col.ID, BlockID: rowID, Type: av.KeyTypeUpdated} + } + + tableRow.Cells = append(tableRow.Cells, tableCell) + } + ret.Rows = append(ret.Rows, &tableRow) + } + + // 渲染自动生成的列值,比如模板列、创建时间列和更新时间列 + for _, row := range ret.Rows { + for _, cell := range row.Cells { + switch cell.ValueType { + case av.KeyTypeTemplate: // 渲染模板列 + keyValues := rows[row.ID] + ial := map[string]string{} + block := row.GetBlockValue() + if !block.IsDetached { + ial = cache.GetBlockIAL(row.ID) + if nil == ial { + ial = map[string]string{} + } + } + content := renderTemplateCol(ial, cell.Value.Template.Content, keyValues) + cell.Value.Template.Content = content + case av.KeyTypeCreated: // 渲染创建时间 + createdStr := row.ID[:len("20060102150405")] + created, parseErr := time.ParseInLocation("20060102150405", createdStr, time.Local) + if nil == parseErr { + cell.Value.Created = av.NewFormattedValueCreated(created.UnixMilli(), 0, av.CreatedFormatNone) + cell.Value.Created.IsNotEmpty = true + } else { + cell.Value.Created = av.NewFormattedValueCreated(time.Now().UnixMilli(), 0, av.CreatedFormatNone) + } + case av.KeyTypeUpdated: // 渲染更新时间 + ial := map[string]string{} + block := row.GetBlockValue() + if !block.IsDetached { + ial = cache.GetBlockIAL(row.ID) + if nil == ial { + ial = map[string]string{} + } + } + updatedStr := ial["updated"] + if "" == updatedStr { + block := row.GetBlockValue() + cell.Value.Updated = av.NewFormattedValueUpdated(block.Block.Updated, 0, av.UpdatedFormatNone) + cell.Value.Updated.IsNotEmpty = true + } else { + updated, parseErr := time.ParseInLocation("20060102150405", updatedStr, time.Local) + if nil == parseErr { + cell.Value.Updated = av.NewFormattedValueUpdated(updated.UnixMilli(), 0, av.UpdatedFormatNone) + cell.Value.Updated.IsNotEmpty = true + } else { + cell.Value.Updated = av.NewFormattedValueUpdated(time.Now().UnixMilli(), 0, av.UpdatedFormatNone) + } + } + } + } + } + return +} + +func renderTemplateCol(ial map[string]string, tplContent string, rowValues []*av.KeyValues) string { + if "" == ial["id"] { + block := getRowBlockValue(rowValues) + ial["id"] = block.Block.ID + } + if "" == ial["updated"] { + block := getRowBlockValue(rowValues) + ial["updated"] = time.UnixMilli(block.Block.Updated).Format("20060102150405") + } + + funcMap := sprig.TxtFuncMap() + goTpl := template.New("").Delims(".action{", "}") + tpl, tplErr := goTpl.Funcs(funcMap).Parse(tplContent) + if nil != tplErr { + logging.LogWarnf("parse template [%s] failed: %s", tplContent, tplErr) + return "" + } + + buf := &bytes.Buffer{} + dataModel := map[string]interface{}{} // 复制一份 IAL 以避免修改原始数据 + for k, v := range ial { + dataModel[k] = v + + // Database template column supports `created` and `updated` built-in variables https://github.com/siyuan-note/siyuan/issues/9364 + createdStr := ial["id"] + if "" != createdStr { + createdStr = createdStr[:len("20060102150405")] + } + created, parseErr := time.ParseInLocation("20060102150405", createdStr, time.Local) + if nil == parseErr { + dataModel["created"] = created + } else { + logging.LogWarnf("parse created [%s] failed: %s", createdStr, parseErr) + dataModel["created"] = time.Now() + } + updatedStr := ial["updated"] + updated, parseErr := time.ParseInLocation("20060102150405", updatedStr, time.Local) + if nil == parseErr { + dataModel["updated"] = updated + } else { + dataModel["updated"] = time.Now() + } + } + for _, rowValue := range rowValues { + if 0 < len(rowValue.Values) { + v := rowValue.Values[0] + if av.KeyTypeNumber == v.Type { + dataModel[rowValue.Key.Name] = v.Number.Content + } else { + dataModel[rowValue.Key.Name] = v.String() + } + } + } + if err := tpl.Execute(buf, dataModel); nil != err { + logging.LogWarnf("execute template [%s] failed: %s", tplContent, err) + } + return buf.String() +} + +func getRowBlockValue(keyValues []*av.KeyValues) (ret *av.Value) { + for _, kv := range keyValues { + if av.KeyTypeBlock == kv.Key.Type && 0 < len(kv.Values) { + ret = kv.Values[0] + break + } + } + return +} diff --git a/kernel/util/working.go b/kernel/util/working.go index 7e44781c49b..c3b44e33660 100644 --- a/kernel/util/working.go +++ b/kernel/util/working.go @@ -380,8 +380,7 @@ func initMime() { mime.AddExtensionType(".json", "application/json") mime.AddExtensionType(".html", "text/html") - // 某些系统上下载资源文件后打开是 zip - // https://github.com/siyuan-note/siyuan/issues/6347 + // 某些系统上下载资源文件后打开是 zip https://github.com/siyuan-note/siyuan/issues/6347 mime.AddExtensionType(".doc", "application/msword") mime.AddExtensionType(".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document") mime.AddExtensionType(".xls", "application/vnd.ms-excel") @@ -391,6 +390,9 @@ func initMime() { mime.AddExtensionType(".dwf", "drawing/x-dwf") mime.AddExtensionType(".pdf", "application/pdf") + // 某些系统上无法显示 SVG 图片 SVG images cannot be displayed on some systems https://github.com/siyuan-note/siyuan/issues/9413 + mime.AddExtensionType(".svg", "image/svg+xml") + // 文档数据文件 mime.AddExtensionType(".sy", "application/json") } diff --git a/scripts/win-build.bat b/scripts/win-build.bat index 42f924a76c9..68f1b64acca 100644 --- a/scripts/win-build.bat +++ b/scripts/win-build.bat @@ -33,10 +33,9 @@ echo 'Building Electron' cd app call pnpm run dist -cd .. - -echo 'Building Appx' -echo 'Building Appx should be disabled if you do not need it. Not configured correctly will lead to build failures' -cd . > app\build\win-unpacked\resources\ms-store -electron-windows-store --input-directory app\build\win-unpacked --output-directory app\build\ --package-version 1.0.0.0 --package-name SiYuan --manifest app\appx\AppxManifest.xml --assets app\appx\assets\ --make-pri true +@REM cd .. +@REM echo 'Building Appx' +@REM echo 'Building Appx should be disabled if you do not need it. Not configured correctly will lead to build failures' +@REM cd . > app\build\win-unpacked\resources\ms-store +@REM electron-windows-store --input-directory app\build\win-unpacked --output-directory app\build\ --package-version 1.0.0.0 --package-name SiYuan --manifest app\appx\AppxManifest.xml --assets app\appx\assets\ --make-pri true