소스 검색

Merge remote-tracking branch 'origin/dev' into dev

Vanessa 1 년 전
부모
커밋
fa6ee1b1ab

+ 4 - 0
app/changelogs/v2.10.12/v2.10.12.md

@@ -30,6 +30,8 @@ Below are the detailed changes in this version.
 * [After `Alt+[`, `Ctrl+Enter` and `Ctrl+Click` generates a dynamic ref](https://github.com/siyuan-note/siyuan/issues/9480)
 * [Add some icons](https://github.com/siyuan-note/siyuan/issues/9481)
 * [Improve KaTex macros parsing](https://github.com/siyuan-note/siyuan/issues/9485)
+* [Improve priority of folding processing when headings and super blocks are mixed](https://github.com/siyuan-note/siyuan/issues/9488)
+* [Remove invisible characters when entering document title](https://github.com/siyuan-note/siyuan/issues/9493)
 
 ### Bugfix
 
@@ -37,6 +39,8 @@ Below are the detailed changes in this version.
 
 ### Development
 
+* [Enhance compatibility and the plugin command cannot be set to empty](https://github.com/siyuan-note/siyuan/issues/9231)
 * [Add plugin event bus `paste`](https://github.com/siyuan-note/siyuan/issues/9452)
 * [Improve database date column "Is Empty" filtering](https://github.com/siyuan-note/siyuan/issues/9463)
 * [Deprecated `loaded-protyle` use `loaded-protyle-static` instead](https://github.com/siyuan-note/siyuan/issues/9468)
+* [Shallow clone the corresponding database when the template contains database blocks](https://github.com/siyuan-note/siyuan/issues/9494)

+ 4 - 0
app/changelogs/v2.10.12/v2.10.12_zh_CHT.md

@@ -30,6 +30,8 @@
 * [按 `Alt+[` 後支援透過 `Ctrl+Enter` 和 `Ctrl+Click` 產生動態錨文本](https://github.com/siyuan-note/siyuan/issues/9480)
 * [新增一些圖示](https://github.com/siyuan-note/siyuan/issues/9481)
 * [改進 KaTex 宏定義解析](https://github.com/siyuan-note/siyuan/issues/9485)
+* [改進標題區塊和超級區塊混用時折疊處理的優先順序](https://github.com/siyuan-note/siyuan/issues/9488)
+* [輸入文件標題時移除不可見字元](https://github.com/siyuan-note/siyuan/issues/9493)
 
 ### 修復缺陷
 
@@ -37,6 +39,8 @@
 
 ### 開發者
 
+* [改進插件命令相容性](https://github.com/siyuan-note/siyuan/issues/9231)
 * [新增插件事件 `paste`](https://github.com/siyuan-note/siyuan/issues/9452)
 * [改進資料庫視圖「為空」過濾](https://github.com/siyuan-note/siyuan/issues/9463)
 * [棄用 `loaded-protyle` 使用 `loaded-protyle-static` 替代](https://github.com/siyuan-note/siyuan/issues/9468)
+* [模板中包含資料庫區塊時淺克隆對應資料庫](https://github.com/siyuan-note/siyuan/issues/9494)

+ 4 - 0
app/changelogs/v2.10.12/v2.10.12_zh_CN.md

@@ -30,6 +30,8 @@
 * [按 `Alt+[` 后支持通过 `Ctrl+Enter` 和 `Ctrl+Click` 生成动态锚文本](https://github.com/siyuan-note/siyuan/issues/9480)
 * [添加一些图标](https://github.com/siyuan-note/siyuan/issues/9481)
 * [改进 KaTex 宏定义解析](https://github.com/siyuan-note/siyuan/issues/9485)
+* [改进标题块和超级块混用时折叠处理的优先级](https://github.com/siyuan-note/siyuan/issues/9488)
+* [输入文档标题时移除不可见字符](https://github.com/siyuan-note/siyuan/issues/9493)
 
 ### 修复缺陷
 
@@ -37,6 +39,8 @@
 
 ### 开发者
 
+* [改进插件命令兼容性](https://github.com/siyuan-note/siyuan/issues/9231)
 * [添加插件事件 `paste`](https://github.com/siyuan-note/siyuan/issues/9452)
 * [改进数据库视图“为空”过滤](https://github.com/siyuan-note/siyuan/issues/9463)
 * [弃用 `loaded-protyle` 使用 `loaded-protyle-static` 替代](https://github.com/siyuan-note/siyuan/issues/9468)
+* [模板中包含数据库块时浅克隆对应数据库](https://github.com/siyuan-note/siyuan/issues/9494)

+ 7 - 23
app/src/protyle/util/heading.ts

@@ -2,30 +2,14 @@ export const removeFoldHeading = (nodeElement: Element) => {
     const nodeH = parseInt(nodeElement.getAttribute("data-subtype").substr(1));
     let nextElement = nodeElement.nextElementSibling;
     while (nextElement) {
-        if (nextElement.classList.contains("sb")) {
-            let nextFirstElement = nextElement.firstElementChild;
-            while (nextFirstElement && nextFirstElement.classList.contains("sb")) {
-                nextFirstElement = nextFirstElement.firstElementChild;
-            }
-            if ((nextFirstElement.getAttribute("data-type") === "NodeHeading" &&
-                    parseInt(nextFirstElement.getAttribute("data-subtype").substr(1)) > nodeH) ||
-                nextFirstElement.getAttribute("data-type") !== "NodeHeading") {
-                const tempElement = nextElement;
-                nextElement = nextElement.nextElementSibling;
-                tempElement.remove();
-            } else {
-                break;
-            }
+        const currentH = parseInt(nextElement.getAttribute("data-subtype")?.substr(1));
+        if (!nextElement.classList.contains("protyle-attr") && // 超级块末尾为属性
+            (isNaN(currentH) || currentH > nodeH)) {
+            const tempElement = nextElement;
+            nextElement = nextElement.nextElementSibling;
+            tempElement.remove();
         } else {
-            const currentH = parseInt(nextElement.getAttribute("data-subtype")?.substr(1));
-            if (!nextElement.classList.contains("protyle-attr") && // 超级块末尾为属性
-                (isNaN(currentH) || currentH > nodeH)) {
-                const tempElement = nextElement;
-                nextElement = nextElement.nextElementSibling;
-                tempElement.remove();
-            } else {
-                break;
-            }
+            break;
         }
     }
 };

+ 6 - 1
kernel/api/template.go

@@ -80,7 +80,12 @@ func renderTemplate(c *gin.Context) {
 		return
 	}
 
-	content, err := model.RenderTemplate(p, id)
+	preview := false
+	if previewArg := arg["preview"]; nil != previewArg {
+		preview = previewArg.(bool)
+	}
+
+	content, err := model.RenderTemplate(p, id, preview)
 	if nil != err {
 		ret.Code = -1
 		ret.Msg = util.EscapeHTML(err.Error())

+ 2 - 2
kernel/api/workspace.go

@@ -246,10 +246,10 @@ func getWorkspaces(c *gin.Context) {
 		}
 	}
 	sort.Slice(openedWorkspaces, func(i, j int) bool {
-		return natsort.Compare(util.RemoveEmoji(filepath.Base(openedWorkspaces[i].Path)), util.RemoveEmoji(filepath.Base(openedWorkspaces[j].Path)))
+		return natsort.Compare(util.RemoveEmojiInvisible(filepath.Base(openedWorkspaces[i].Path)), util.RemoveEmojiInvisible(filepath.Base(openedWorkspaces[j].Path)))
 	})
 	sort.Slice(closedWorkspaces, func(i, j int) bool {
-		return natsort.Compare(util.RemoveEmoji(filepath.Base(closedWorkspaces[i].Path)), util.RemoveEmoji(filepath.Base(closedWorkspaces[j].Path)))
+		return natsort.Compare(util.RemoveEmojiInvisible(filepath.Base(closedWorkspaces[i].Path)), util.RemoveEmojiInvisible(filepath.Base(closedWorkspaces[j].Path)))
 	})
 	workspaces = append(workspaces, openedWorkspaces...)
 	workspaces = append(workspaces, closedWorkspaces...)

+ 39 - 0
kernel/av/av.go

@@ -46,6 +46,45 @@ type AttributeView struct {
 	Views     []*View      `json:"views"`     // 视图
 }
 
+func ShallowCloneAttributeView(av *AttributeView) (ret *AttributeView) {
+	ret = &AttributeView{}
+	data, err := gulu.JSON.MarshalJSON(av)
+	if nil != err {
+		logging.LogErrorf("marshal attribute view [%s] failed: %s", av.ID, err)
+		return nil
+	}
+	if err = gulu.JSON.UnmarshalJSON(data, ret); nil != err {
+		logging.LogErrorf("unmarshal attribute view [%s] failed: %s", av.ID, err)
+		return nil
+	}
+
+	ret.ID = ast.NewNodeID()
+	view, err := ret.GetView()
+	if nil == err {
+		view.ID = ast.NewNodeID()
+		ret.ViewID = view.ID
+	} else {
+		view = NewView()
+		ret.ViewID = view.ID
+		ret.Views = append(ret.Views, view)
+	}
+
+	keyIDMap := map[string]string{}
+	for _, kv := range ret.KeyValues {
+		newID := ast.NewNodeID()
+		keyIDMap[kv.Key.ID] = newID
+		kv.Key.ID = newID
+		kv.Values = []*Value{}
+	}
+
+	view.Table.ID = ast.NewNodeID()
+	for _, column := range view.Table.Columns {
+		column.ID = keyIDMap[column.ID]
+	}
+	view.Table.RowIDs = []string{}
+	return
+}
+
 // KeyValues 描述了属性视图属性列值的结构。
 type KeyValues struct {
 	Key    *Key     `json:"key"`              // 属性视图属性列

+ 1 - 1
kernel/go.mod

@@ -6,7 +6,7 @@ require (
 	code.sajari.com/docconv v1.3.7
 	github.com/88250/clipboard v0.1.5
 	github.com/88250/epub v0.0.0-20230830085737-c19055cd1f48
-	github.com/88250/gulu v1.2.3-0.20230615033005-b519d6875346
+	github.com/88250/gulu v1.2.3-0.20231023172823-f152fc5d93ef
 	github.com/88250/lute v1.7.6-0.20231011010815-eeb59b60f14f
 	github.com/88250/pdfcpu v0.3.14-0.20230401044135-c7369a99720c
 	github.com/88250/vitess-sqlparser v0.0.0-20210205111146-56a2ded2aba1

+ 2 - 2
kernel/go.sum

@@ -8,8 +8,8 @@ github.com/88250/epub v0.0.0-20230830085737-c19055cd1f48 h1:qiE88Pw/9GG8hvMfpfB4
 github.com/88250/epub v0.0.0-20230830085737-c19055cd1f48/go.mod h1:UgVSq5iO9pOvqs3hIGNVk6WXDiAB0v3Dlg4nssQJ7W4=
 github.com/88250/go-sqlite3 v1.14.13-0.20220714142610-fbbda1ee84f5 h1:8HdZozCsXSiEXYAo8Zbi/r2Ld6Dd4MmGHgir3EaSuHQ=
 github.com/88250/go-sqlite3 v1.14.13-0.20220714142610-fbbda1ee84f5/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
-github.com/88250/gulu v1.2.3-0.20230615033005-b519d6875346 h1:U0wZN6zuf+vUhzdHC6DbZE/bY5FUt3FuCwn/KgObV44=
-github.com/88250/gulu v1.2.3-0.20230615033005-b519d6875346/go.mod h1:pTWnjt+6qUqNnP9xltswsJxgCBVu3C7eW09u48LWX0k=
+github.com/88250/gulu v1.2.3-0.20231023172823-f152fc5d93ef h1:LVJO3aj18v63T2whGGt3gL5imkJk+7ujPfFv38X7Oi8=
+github.com/88250/gulu v1.2.3-0.20231023172823-f152fc5d93ef/go.mod h1:pTWnjt+6qUqNnP9xltswsJxgCBVu3C7eW09u48LWX0k=
 github.com/88250/lute v1.7.6-0.20231011010815-eeb59b60f14f h1:3WsWusMYa05xg5bTG9hRe3WjEyoWboAHFa029Hbi0EQ=
 github.com/88250/lute v1.7.6-0.20231011010815-eeb59b60f14f/go.mod h1:+wUqx/1kdFDbWtxn9LYJlaCOAeol2pjSO6w+WJTVQsg=
 github.com/88250/pdfcpu v0.3.14-0.20230401044135-c7369a99720c h1:Dl/8S9iLyPMTElnWIBxmjaLiWrkI5P4a21ivwAn5pU0=

+ 8 - 8
kernel/model/backlink.go

@@ -270,13 +270,13 @@ func GetBacklink2(id, keyword, mentionKeyword string, sortMode, mentionSortMode
 		case util.SortModeCreatedASC:
 			return backlinks[i].Created < backlinks[j].Created
 		case util.SortModeNameDESC:
-			return util.PinYinCompare(util.RemoveEmoji(backlinks[j].Name), util.RemoveEmoji(backlinks[i].Name))
+			return util.PinYinCompare(util.RemoveEmojiInvisible(backlinks[j].Name), util.RemoveEmojiInvisible(backlinks[i].Name))
 		case util.SortModeNameASC:
-			return util.PinYinCompare(util.RemoveEmoji(backlinks[i].Name), util.RemoveEmoji(backlinks[j].Name))
+			return util.PinYinCompare(util.RemoveEmojiInvisible(backlinks[i].Name), util.RemoveEmojiInvisible(backlinks[j].Name))
 		case util.SortModeAlphanumDESC:
-			return natsort.Compare(util.RemoveEmoji(backlinks[j].Name), util.RemoveEmoji(backlinks[i].Name))
+			return natsort.Compare(util.RemoveEmojiInvisible(backlinks[j].Name), util.RemoveEmojiInvisible(backlinks[i].Name))
 		case util.SortModeAlphanumASC:
-			return natsort.Compare(util.RemoveEmoji(backlinks[i].Name), util.RemoveEmoji(backlinks[j].Name))
+			return natsort.Compare(util.RemoveEmojiInvisible(backlinks[i].Name), util.RemoveEmojiInvisible(backlinks[j].Name))
 		}
 		return backlinks[i].ID > backlinks[j].ID
 	})
@@ -299,13 +299,13 @@ func GetBacklink2(id, keyword, mentionKeyword string, sortMode, mentionSortMode
 		case util.SortModeCreatedASC:
 			return backmentions[i].Created < backmentions[j].Created
 		case util.SortModeNameDESC:
-			return util.PinYinCompare(util.RemoveEmoji(backmentions[j].Name), util.RemoveEmoji(backmentions[i].Name))
+			return util.PinYinCompare(util.RemoveEmojiInvisible(backmentions[j].Name), util.RemoveEmojiInvisible(backmentions[i].Name))
 		case util.SortModeNameASC:
-			return util.PinYinCompare(util.RemoveEmoji(backmentions[i].Name), util.RemoveEmoji(backmentions[j].Name))
+			return util.PinYinCompare(util.RemoveEmojiInvisible(backmentions[i].Name), util.RemoveEmojiInvisible(backmentions[j].Name))
 		case util.SortModeAlphanumDESC:
-			return natsort.Compare(util.RemoveEmoji(backmentions[j].Name), util.RemoveEmoji(backmentions[i].Name))
+			return natsort.Compare(util.RemoveEmojiInvisible(backmentions[j].Name), util.RemoveEmojiInvisible(backmentions[i].Name))
 		case util.SortModeAlphanumASC:
-			return natsort.Compare(util.RemoveEmoji(backmentions[i].Name), util.RemoveEmoji(backmentions[j].Name))
+			return natsort.Compare(util.RemoveEmojiInvisible(backmentions[i].Name), util.RemoveEmojiInvisible(backmentions[j].Name))
 		}
 		return backmentions[i].ID > backmentions[j].ID
 	})

+ 4 - 4
kernel/model/box.go

@@ -152,21 +152,21 @@ func ListNotebooks() (ret []*Box, err error) {
 	switch Conf.FileTree.Sort {
 	case util.SortModeNameASC:
 		sort.Slice(ret, func(i, j int) bool {
-			return util.PinYinCompare(util.RemoveEmoji(ret[i].Name), util.RemoveEmoji(ret[j].Name))
+			return util.PinYinCompare(util.RemoveEmojiInvisible(ret[i].Name), util.RemoveEmojiInvisible(ret[j].Name))
 		})
 	case util.SortModeNameDESC:
 		sort.Slice(ret, func(i, j int) bool {
-			return util.PinYinCompare(util.RemoveEmoji(ret[j].Name), util.RemoveEmoji(ret[i].Name))
+			return util.PinYinCompare(util.RemoveEmojiInvisible(ret[j].Name), util.RemoveEmojiInvisible(ret[i].Name))
 		})
 	case util.SortModeUpdatedASC:
 	case util.SortModeUpdatedDESC:
 	case util.SortModeAlphanumASC:
 		sort.Slice(ret, func(i, j int) bool {
-			return natsort.Compare(util.RemoveEmoji(ret[i].Name), util.RemoveEmoji(ret[j].Name))
+			return natsort.Compare(util.RemoveEmojiInvisible(ret[i].Name), util.RemoveEmojiInvisible(ret[j].Name))
 		})
 	case util.SortModeAlphanumDESC:
 		sort.Slice(ret, func(i, j int) bool {
-			return natsort.Compare(util.RemoveEmoji(ret[j].Name), util.RemoveEmoji(ret[i].Name))
+			return natsort.Compare(util.RemoveEmojiInvisible(ret[j].Name), util.RemoveEmojiInvisible(ret[i].Name))
 		})
 	case util.SortModeCustom:
 		sort.Slice(ret, func(i, j int) bool { return ret[i].Sort < ret[j].Sort })

+ 5 - 11
kernel/model/file.go

@@ -374,11 +374,11 @@ func ListDocTree(boxID, listPath string, sortMode int, flashcard, showHidden boo
 	switch sortMode {
 	case util.SortModeNameASC:
 		sort.Slice(docs, func(i, j int) bool {
-			return util.PinYinCompare(util.RemoveEmoji(docs[i].Name), util.RemoveEmoji(docs[j].Name))
+			return util.PinYinCompare(util.RemoveEmojiInvisible(docs[i].Name), util.RemoveEmojiInvisible(docs[j].Name))
 		})
 	case util.SortModeNameDESC:
 		sort.Slice(docs, func(i, j int) bool {
-			return util.PinYinCompare(util.RemoveEmoji(docs[j].Name), util.RemoveEmoji(docs[i].Name))
+			return util.PinYinCompare(util.RemoveEmojiInvisible(docs[j].Name), util.RemoveEmojiInvisible(docs[i].Name))
 		})
 	case util.SortModeUpdatedASC:
 		sort.Slice(docs, func(i, j int) bool { return docs[i].Mtime < docs[j].Mtime })
@@ -386,11 +386,11 @@ func ListDocTree(boxID, listPath string, sortMode int, flashcard, showHidden boo
 		sort.Slice(docs, func(i, j int) bool { return docs[i].Mtime > docs[j].Mtime })
 	case util.SortModeAlphanumASC:
 		sort.Slice(docs, func(i, j int) bool {
-			return natsort.Compare(util.RemoveEmoji(docs[i].Name), util.RemoveEmoji(docs[j].Name))
+			return natsort.Compare(util.RemoveEmojiInvisible(docs[i].Name), util.RemoveEmojiInvisible(docs[j].Name))
 		})
 	case util.SortModeAlphanumDESC:
 		sort.Slice(docs, func(i, j int) bool {
-			return natsort.Compare(util.RemoveEmoji(docs[j].Name), util.RemoveEmoji(docs[i].Name))
+			return natsort.Compare(util.RemoveEmojiInvisible(docs[j].Name), util.RemoveEmojiInvisible(docs[i].Name))
 		})
 	case util.SortModeCustom:
 		fileTreeFiles := docs
@@ -848,12 +848,6 @@ func loadNodesByMode(node *ast.Node, inputIndex, mode, size int, isDoc, isHeadin
 					if n.HeadingLevel <= level {
 						break
 					}
-				} else if ast.NodeSuperBlock == n.Type {
-					if h := treenode.SuperBlockHeading(n); nil != h {
-						if level >= h.HeadingLevel {
-							break
-						}
-					}
 				}
 				nodes = append(nodes, n)
 				count++
@@ -1454,7 +1448,7 @@ func CreateDailyNote(boxID string) (p string, existed bool, err error) {
 		if !gulu.File.IsExist(tplPath) {
 			logging.LogWarnf("not found daily note template [%s]", tplPath)
 		} else {
-			dom, err = renderTemplate(tplPath, id)
+			dom, err = renderTemplate(tplPath, id, false)
 			if nil != err {
 				logging.LogWarnf("render daily note template [%s] failed: %s", boxConf.DailyNoteTemplatePath, err)
 			}

+ 12 - 6
kernel/model/heading.go

@@ -46,11 +46,14 @@ func (tx *Transaction) doFoldHeading(operation *Operation) (ret *TxErr) {
 		return &TxErr{code: TxErrCodeBlockNotFound, id: headingID}
 	}
 
-	children := treenode.HeadingChildren4Folding(heading)
+	children := treenode.HeadingChildren(heading)
 	for _, child := range children {
 		childrenIDs = append(childrenIDs, child.ID)
-		child.SetIALAttr("fold", "1")
-		child.SetIALAttr("heading-fold", "1")
+		ast.Walk(child, func(n *ast.Node, entering bool) ast.WalkStatus {
+			n.SetIALAttr("fold", "1")
+			n.SetIALAttr("heading-fold", "1")
+			return ast.WalkContinue
+		})
 	}
 	heading.SetIALAttr("fold", "1")
 	if err = tx.writeTree(tree); nil != err {
@@ -80,10 +83,13 @@ func (tx *Transaction) doUnfoldHeading(operation *Operation) (ret *TxErr) {
 		return &TxErr{code: TxErrCodeBlockNotFound, id: headingID}
 	}
 
-	children := treenode.HeadingChildren4Folding(heading)
+	children := treenode.HeadingChildren(heading)
 	for _, child := range children {
-		child.RemoveIALAttr("heading-fold")
-		child.RemoveIALAttr("fold")
+		ast.Walk(child, func(n *ast.Node, entering bool) ast.WalkStatus {
+			n.RemoveIALAttr("heading-fold")
+			n.RemoveIALAttr("fold")
+			return ast.WalkContinue
+		})
 	}
 	heading.RemoveIALAttr("fold")
 	heading.RemoveIALAttr("heading-fold")

+ 8 - 1
kernel/model/plugin.go

@@ -74,10 +74,17 @@ func SetPetalEnabled(name string, enabled bool, frontend string) (ret *Petal, er
 func LoadPetals(frontend string) (ret []*Petal) {
 	ret = []*Petal{}
 
-	if Conf.Bazaar.PetalDisabled || !Conf.Bazaar.Trust {
+	if Conf.Bazaar.PetalDisabled {
 		return
 	}
 
+	if !Conf.Bazaar.Trust {
+		// 移动端没有集市模块,所以要默认开启,桌面端和 Docker 容器需要用户手动确认过信任后才能开启
+		if util.ContainerStd == util.Container || util.ContainerDocker == util.Container {
+			return
+		}
+	}
+
 	petals := getPetals()
 	for _, petal := range petals {
 		_, petal.DisplayName, petal.Incompatible = bazaar.ParseInstalledPlugin(petal.Name, frontend)

+ 5 - 5
kernel/model/tag.go

@@ -228,19 +228,19 @@ func sortTags(tags Tags) {
 	switch Conf.Tag.Sort {
 	case util.SortModeNameASC:
 		sort.Slice(tags, func(i, j int) bool {
-			return util.PinYinCompare(util.RemoveEmoji(tags[i].Name), util.RemoveEmoji(tags[j].Name))
+			return util.PinYinCompare(util.RemoveEmojiInvisible(tags[i].Name), util.RemoveEmojiInvisible(tags[j].Name))
 		})
 	case util.SortModeNameDESC:
 		sort.Slice(tags, func(j, i int) bool {
-			return util.PinYinCompare(util.RemoveEmoji(tags[i].Name), util.RemoveEmoji(tags[j].Name))
+			return util.PinYinCompare(util.RemoveEmojiInvisible(tags[i].Name), util.RemoveEmojiInvisible(tags[j].Name))
 		})
 	case util.SortModeAlphanumASC:
 		sort.Slice(tags, func(i, j int) bool {
-			return natsort.Compare(util.RemoveEmoji((tags)[i].Name), util.RemoveEmoji((tags)[j].Name))
+			return natsort.Compare(util.RemoveEmojiInvisible((tags)[i].Name), util.RemoveEmojiInvisible((tags)[j].Name))
 		})
 	case util.SortModeAlphanumDESC:
 		sort.Slice(tags, func(i, j int) bool {
-			return natsort.Compare(util.RemoveEmoji((tags)[j].Name), util.RemoveEmoji((tags)[i].Name))
+			return natsort.Compare(util.RemoveEmojiInvisible((tags)[j].Name), util.RemoveEmojiInvisible((tags)[i].Name))
 		})
 	case util.SortModeRefCountASC:
 		sort.Slice(tags, func(i, j int) bool { return (tags)[i].Count < (tags)[j].Count })
@@ -248,7 +248,7 @@ func sortTags(tags Tags) {
 		sort.Slice(tags, func(i, j int) bool { return (tags)[i].Count > (tags)[j].Count })
 	default:
 		sort.Slice(tags, func(i, j int) bool {
-			return natsort.Compare(util.RemoveEmoji((tags)[i].Name), util.RemoveEmoji((tags)[j].Name))
+			return natsort.Compare(util.RemoveEmojiInvisible((tags)[i].Name), util.RemoveEmojiInvisible((tags)[j].Name))
 		})
 	}
 }

+ 23 - 3
kernel/model/template.go

@@ -20,6 +20,7 @@ import (
 	"bytes"
 	"errors"
 	"fmt"
+	"github.com/siyuan-note/siyuan/kernel/av"
 	"io/fs"
 	"os"
 	"path/filepath"
@@ -163,11 +164,11 @@ func DocSaveAsTemplate(id, name string, overwrite bool) (code int, err error) {
 	return
 }
 
-func RenderTemplate(p, id string) (string, error) {
-	return renderTemplate(p, id)
+func RenderTemplate(p, id string, preview bool) (string, error) {
+	return renderTemplate(p, id, preview)
 }
 
-func renderTemplate(p, id string) (string, error) {
+func renderTemplate(p, id string, preview bool) (string, error) {
 	tree, err := loadTreeByBlockID(id)
 	if nil != err {
 		return "", err
@@ -277,6 +278,25 @@ func renderTemplate(p, id string) (string, error) {
 				n.TextMarkInlineMathContent = strings.ReplaceAll(n.TextMarkInlineMathContent, "|", "&#124;")
 			}
 		}
+
+		if ast.NodeAttributeView == n.Type {
+			// 重新生成数据库视图
+			attrView, parseErr := av.ParseAttributeView(n.AttributeViewID)
+			if nil != parseErr {
+				logging.LogErrorf("parse attribute view [%s] failed: %s", n.AttributeViewID, parseErr)
+			} else {
+				cloned := av.ShallowCloneAttributeView(attrView)
+				if nil != cloned {
+					n.AttributeViewID = cloned.ID
+					if !preview {
+						if saveErr := av.SaveAttributeView(cloned); nil != saveErr {
+							logging.LogErrorf("save attribute view [%s] failed: %s", cloned.ID, saveErr)
+						}
+					}
+				}
+			}
+		}
+
 		return ast.WalkContinue
 	})
 	for _, n := range nodesNeedAppendChild {

+ 0 - 6
kernel/sql/database.go

@@ -881,12 +881,6 @@ func heading(node *ast.Node) *ast.Node {
 	currentLevel := 16
 	if ast.NodeHeading == node.Type {
 		currentLevel = node.HeadingLevel
-	} else if ast.NodeSuperBlock == node.Type {
-		superBlockHeading := treenode.SuperBlockHeading(node)
-		if nil != superBlockHeading {
-			node = superBlockHeading
-			currentLevel = node.HeadingLevel
-		}
 	}
 
 	for prev := node.Previous; nil != prev; prev = prev.Previous {

+ 0 - 71
kernel/treenode/heading.go

@@ -62,20 +62,6 @@ func IsInFoldedHeading(node, currentHeading *ast.Node) bool {
 		return false
 	}
 
-	if ast.NodeSuperBlock == node.Type {
-		// The super block below the folded heading contains headings of the same level and cannot be loaded https://github.com/siyuan-note/siyuan/issues/9162
-		if nil == currentHeading {
-			return false
-		}
-
-		sbChildHeading := SuperBlockHeading(node)
-		if nil != sbChildHeading {
-			if sbChildHeading.HeadingLevel <= currentHeading.HeadingLevel {
-				return false
-			}
-		}
-	}
-
 	heading := HeadingParent(node)
 	if nil == heading {
 		return false
@@ -99,39 +85,6 @@ func GetHeadingFold(nodes []*ast.Node) (ret []*ast.Node) {
 	return
 }
 
-// HeadingChildren4Folding 获取标题下方所属该标题层级的所有节点,如果遇到超级块的话则递归获取超级块下方的所有节点。
-// 折叠和取消折叠要用这个函数单独处理 https://github.com/siyuan-note/siyuan/issues/9435
-func HeadingChildren4Folding(heading *ast.Node) (ret []*ast.Node) {
-	start := heading.Next
-	if nil == start {
-		return
-	}
-	if ast.NodeKramdownBlockIAL == start.Type {
-		start = start.Next // 跳过 heading 的 IAL
-	}
-
-	currentLevel := heading.HeadingLevel
-	for n := start; nil != n; n = n.Next {
-		if ast.NodeHeading == n.Type {
-			if currentLevel >= n.HeadingLevel {
-				break
-			}
-		} else if ast.NodeSuperBlock == n.Type {
-			ast.Walk(n, func(child *ast.Node, entering bool) ast.WalkStatus {
-				if !entering || !child.IsBlock() {
-					return ast.WalkContinue
-				}
-
-				ret = append(ret, child)
-				return ast.WalkContinue
-			})
-			continue
-		}
-		ret = append(ret, n)
-	}
-	return
-}
-
 func HeadingChildren(heading *ast.Node) (ret []*ast.Node) {
 	start := heading.Next
 	if nil == start {
@@ -147,36 +100,12 @@ func HeadingChildren(heading *ast.Node) (ret []*ast.Node) {
 			if currentLevel >= n.HeadingLevel {
 				break
 			}
-		} else if ast.NodeSuperBlock == n.Type {
-			if h := SuperBlockHeading(n); nil != h {
-				if currentLevel >= h.HeadingLevel {
-					break
-				}
-			}
-		} else if ast.NodeSuperBlockCloseMarker == n.Type {
-			continue
 		}
 		ret = append(ret, n)
 	}
 	return
 }
 
-func SuperBlockHeading(sb *ast.Node) *ast.Node {
-	c := sb.FirstChild.Next.Next
-	if nil == c {
-		return nil
-	}
-
-	if ast.NodeHeading == c.Type {
-		return c
-	}
-
-	if ast.NodeSuperBlock == c.Type {
-		return SuperBlockHeading(c)
-	}
-	return nil
-}
-
 func SuperBlockLastHeading(sb *ast.Node) *ast.Node {
 	headings := sb.ChildrenByType(ast.NodeHeading)
 	if 0 < len(headings) {

+ 9 - 3
kernel/util/emoji.go → kernel/util/rune.go

@@ -16,7 +16,11 @@
 
 package util
 
-import "regexp"
+import (
+	"regexp"
+
+	"github.com/88250/gulu"
+)
 
 var emojiRegex = regexp.MustCompile(`/([0-9#][\x{20E3}])|` +
 	`[\x{00ae}\x{00a9}\x{203C}\x{2047}\x{2048}\x{2049}\x{3030}\x{303D}\x{2139}\x{2122}\x{3297}\x{3299}]|` +
@@ -37,6 +41,8 @@ var emojiRegex = regexp.MustCompile(`/([0-9#][\x{20E3}])|` +
 	`[\x{2700}-\x{27BF}]|` +
 	`[\x{10000}-\x{E01EF}]`)
 
-func RemoveEmoji(text string) string {
-	return emojiRegex.ReplaceAllString(text, "")
+func RemoveEmojiInvisible(text string) (ret string) {
+	ret = emojiRegex.ReplaceAllString(text, "")
+	ret = gulu.Str.RemoveInvisible(ret)
+	return
 }

+ 1 - 1
kernel/util/working.go

@@ -222,7 +222,7 @@ func initWorkspaceDir(workspaceArg string) {
 	}
 
 	if !gulu.File.IsDir(WorkspaceDir) {
-		logging.LogWarnf("use the default workspace [%s] since the specified workspace [%s] is not a dir", WorkspaceDir, defaultWorkspaceDir)
+		logging.LogWarnf("use the default workspace [%s] since the specified workspace [%s] is not a dir", defaultWorkspaceDir, WorkspaceDir)
 		if err := os.MkdirAll(defaultWorkspaceDir, 0755); nil != err && !os.IsExist(err) {
 			logging.LogErrorf("create default workspace folder [%s] failed: %s", defaultWorkspaceDir, err)
 			os.Exit(logging.ExitCodeInitWorkspaceErr)