Browse Source

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

Vanessa 2 years ago
parent
commit
c6b3083a24

+ 1 - 1
app/appearance/langs/en_US.json

@@ -548,7 +548,7 @@
   "kernelFault1": "Please check if the network connection and kernel process is normal",
   "kernelFault2": "If the problem still occurs after restarting, please report it via <a href=\"https://github.com/siyuan-note/siyuan/issues\" target=\"_blank\">GitHub Issues</a>",
   "fontSize": "Font Size",
-  "fontSizeTip": "The default font size is 16px, this setting only affects the font size display in the editor",
+  "fontSizeTip": "The default font size is 16px, this setting affects the editor and exported PDF/HTML font size display",
   "font1": "This setting only affects the font family display in the editor, choose <code class='fn__code'>Default</code> to use the theme&#039;s font family",
   "newNameFile": "The name of the new subdocument is",
   "newNameSettingFile": "The name of the new document is",

+ 1 - 1
app/appearance/langs/es_ES.json

@@ -548,7 +548,7 @@
   "kernelFault1": "Verifique si la conexión de red y los procesos del kernel son normales",
   "kernelFault2": "Si el problema sigue produciéndose después de reiniciar, comuníquelo a través de <a href=\"https://github.com/siyuan-note/siyuan/issues\" target=\"_blank\">Problemas en GitHub</a>",
   "fontSize": "Tamaño de la fuente",
-  "fontSizeTip": "El tamaño de la fuente por defecto es de 16px, este ajuste sólo afecta a la visualización del tamaño de la fuente en el editor",
+  "fontSizeTip": "El tamaño de fuente predeterminado es 16px, esta configuración afecta el editor y la visualización del tamaño de fuente PDF/HTML exportado",
   "font1": "Este ajuste sólo afecta a la visualización de la familia de fuentes en el editor, elija <code class='fn__code'>Por defecto</code> para utilizar la familia de fuentes del tema",
   "newNameFile": "El nombre del nuevo subdocumento es",
   "newNameSettingFile": "El nombre del nuevo documento es",

+ 1 - 1
app/appearance/langs/fr_FR.json

@@ -548,7 +548,7 @@
   "kernelFault1": "Veuillez vérifier si la connexion réseau et les processus du noyau sont normaux",
   "kernelFault2": "Si le problème persiste après le redémarrage, veuillez le signaler via <a href=\"https://github.com/siyuan-note/siyuan/issues\" target=\"_blank\">GitHub Issues</a>",
   "fontSize": "Taille de la police",
-  "fontSizeTip": "La taille de la police par défaut est de 16px, ce paramètre n'affecte que la taille de la police affichée dans l'éditeur.",
+  "fontSizeTip": "La taille de police par défaut est de 16px, ce paramètre affecte l'éditeur et l'affichage de la taille de police PDF/HTML exportée",
   "font1": "Ce paramètre n'affecte que l'affichage de la famille de polices dans l'éditeur, choisissez <code class='fn__code'>Default</code> pour utiliser la famille de polices du thème.",
   "newNameFile": "Le nom du nouveau sous-document est",
   "newNameSettingFile": "Le nom du nouveau document est",

+ 1 - 1
app/appearance/langs/zh_CHT.json

@@ -548,7 +548,7 @@
   "kernelFault1": "請檢查網絡連接和內核進程是否正常",
   "kernelFault2": "如果重啟後仍然出現該問題,請通過<a href=\"https://ld246.com/article/1649901726096\" target=\"_blank\">這裡回饋</a>",
   "fontSize": "字型大小",
-  "fontSizeTip": "字型大小預設為 16px,該設置僅影響編輯器內字體大小顯示",
+  "fontSizeTip": "字號默認為 16px,該設置影響編輯器和導出 PDF/HTML 字體大小顯示",
   "font1": "該設置僅影響編輯器內字體顯示,選擇 <code class='fn__code'>預設</code> 則使用主題自帶字體",
   "newNameFile": "新建子文檔名為",
   "newNameSettingFile": "新建文檔名為",

+ 1 - 1
app/appearance/langs/zh_CN.json

@@ -548,7 +548,7 @@
   "kernelFault1": "请检查网络连接和内核进程是否正常",
   "kernelFault2": "如果重启后仍然出现该问题,请通过<a href=\"https://ld246.com/article/1649901726096\" target=\"_blank\">这里反馈</a>",
   "fontSize": "字号",
-  "fontSizeTip": "字号默认为 16px,该设置仅影响编辑器内字体大小显示",
+  "fontSizeTip": "字号默认为 16px,该设置影响编辑器和导出 PDF/HTML 字体大小显示",
   "font1": "该设置仅影响编辑器内字体显示,选择 <code class='fn__code'>默认</code> 则使用主题自带字体",
   "newNameFile": "新建子文档名为",
   "newNameSettingFile": "新建文档名为",

+ 26 - 7
kernel/api/filetree.go

@@ -18,6 +18,7 @@ package api
 
 import (
 	"fmt"
+	"math"
 	"net/http"
 	"path"
 	"regexp"
@@ -95,7 +96,7 @@ func heading2Doc(c *gin.Context) {
 
 	name := path.Base(targetPath)
 	box := model.Conf.Box(targetNotebook)
-	files, _, _ := model.ListDocTree(targetNotebook, path.Dir(targetPath), model.Conf.FileTree.Sort)
+	files, _, _ := model.ListDocTree(targetNotebook, path.Dir(targetPath), model.Conf.FileTree.Sort, false, model.Conf.FileTree.MaxListCount)
 	evt := util.NewCmdResult("heading2doc", 0, util.PushModeBroadcast)
 	evt.Data = map[string]interface{}{
 		"box":            box,
@@ -140,7 +141,7 @@ func li2Doc(c *gin.Context) {
 
 	name := path.Base(targetPath)
 	box := model.Conf.Box(targetNotebook)
-	files, _, _ := model.ListDocTree(targetNotebook, path.Dir(targetPath), model.Conf.FileTree.Sort)
+	files, _, _ := model.ListDocTree(targetNotebook, path.Dir(targetPath), model.Conf.FileTree.Sort, false, model.Conf.FileTree.MaxListCount)
 	evt := util.NewCmdResult("li2doc", 0, util.PushModeBroadcast)
 	evt.Data = map[string]interface{}{
 		"box":            box,
@@ -448,7 +449,7 @@ func createDailyNote(c *gin.Context) {
 	evt.AppId = app
 
 	name := path.Base(p)
-	files, _, _ := model.ListDocTree(box.ID, path.Dir(p), model.Conf.FileTree.Sort)
+	files, _, _ := model.ListDocTree(box.ID, path.Dir(p), model.Conf.FileTree.Sort, false, model.Conf.FileTree.MaxListCount)
 	evt.Data = map[string]interface{}{
 		"box":   box,
 		"path":  p,
@@ -590,8 +591,13 @@ func searchDocs(c *gin.Context) {
 		return
 	}
 
+	flashcard := false
+	if arg["flashcard"] != nil {
+		flashcard = arg["flashcard"].(bool)
+	}
+
 	k := arg["k"].(string)
-	ret.Data = model.SearchDocsByKeyword(k)
+	ret.Data = model.SearchDocsByKeyword(k, flashcard)
 }
 
 func listDocsByPath(c *gin.Context) {
@@ -610,13 +616,26 @@ func listDocsByPath(c *gin.Context) {
 	if nil != sortParam {
 		sortMode = int(sortParam.(float64))
 	}
-	files, totals, err := model.ListDocTree(notebook, p, sortMode)
+	flashcard := false
+	if arg["flashcard"] != nil {
+		flashcard = arg["flashcard"].(bool)
+	}
+	maxListCount := model.Conf.FileTree.MaxListCount
+	if arg["maxListCount"] != nil {
+		// API `listDocsByPath` add an optional parameter `maxListCount` https://github.com/siyuan-note/siyuan/issues/7993
+		maxListCount = int(arg["maxListCount"].(float64))
+		if 0 >= maxListCount {
+			maxListCount = math.MaxInt
+		}
+	}
+
+	files, totals, err := model.ListDocTree(notebook, p, sortMode, flashcard, maxListCount)
 	if nil != err {
 		ret.Code = -1
 		ret.Msg = err.Error()
 		return
 	}
-	if model.Conf.FileTree.MaxListCount < totals {
+	if maxListCount < totals {
 		util.PushMsg(fmt.Sprintf(model.Conf.Language(48), len(files)), 7000)
 	}
 
@@ -708,7 +727,7 @@ func getDoc(c *gin.Context) {
 func pushCreate(box *model.Box, p, treeID string, arg map[string]interface{}) {
 	evt := util.NewCmdResult("create", 0, util.PushModeBroadcast)
 	name := path.Base(p)
-	files, _, _ := model.ListDocTree(box.ID, path.Dir(p), model.Conf.FileTree.Sort)
+	files, _, _ := model.ListDocTree(box.ID, path.Dir(p), model.Conf.FileTree.Sort, false, model.Conf.FileTree.MaxListCount)
 	evt.Data = map[string]interface{}{
 		"box":   box,
 		"path":  p,

+ 18 - 2
kernel/api/notebook.go

@@ -308,11 +308,27 @@ func lsNotebooks(c *gin.Context) {
 	ret := gulu.Ret.NewResult()
 	defer c.JSON(http.StatusOK, ret)
 
-	notebooks, err := model.ListNotebooks()
-	if nil != err {
+	arg, ok := util.JsonArg(c, ret)
+	if !ok {
 		return
 	}
 
+	flashcard := false
+	if arg["flashcard"] != nil {
+		flashcard = arg["flashcard"].(bool)
+	}
+
+	var notebooks []*model.Box
+	if flashcard {
+		notebooks = model.GetFlashcardNotebooks()
+	} else {
+		var err error
+		notebooks, err = model.ListNotebooks()
+		if nil != err {
+			return
+		}
+	}
+
 	ret.Data = map[string]interface{}{
 		"notebooks": notebooks,
 	}

+ 2 - 0
kernel/model/assets.go

@@ -107,6 +107,8 @@ func NetImg2LocalAssets(rootID, originalURL string) (err error) {
 				}
 
 				name := filepath.Base(u)
+				name = util.FilterFileName(name)
+				name = util.TruncateLenFileName(name)
 				name = "net-img-" + name
 				name = util.AssetName(name)
 				writePath := filepath.Join(assetsDirPath, name)

+ 1 - 1
kernel/model/export_merge.go

@@ -110,7 +110,7 @@ func loadTreeNodes(box string, p string, level int) (ret []*ast.Node, err error)
 }
 
 func buildBlockChildren(block *Block) (err error) {
-	files, _, err := ListDocTree(block.Box, block.Path, Conf.FileTree.Sort)
+	files, _, err := ListDocTree(block.Box, block.Path, Conf.FileTree.Sort, false, Conf.FileTree.MaxListCount)
 	if nil != err {
 		return
 	}

+ 66 - 15
kernel/model/file.go

@@ -141,9 +141,18 @@ func (box *Box) moveCorruptedData(filePath string) {
 	logging.LogWarnf("moved corrupted data file [%s] to [%s]", filePath, to)
 }
 
-func SearchDocsByKeyword(keyword string) (ret []map[string]string) {
+func SearchDocsByKeyword(keyword string, flashcard bool) (ret []map[string]string) {
 	ret = []map[string]string{}
 
+	var deckBlockIDs []string
+	if flashcard {
+		deck := Decks[builtinDeckID]
+		if nil != deck {
+			return
+		}
+		deckBlockIDs = deck.GetBlockIDs()
+	}
+
 	openedBoxes := Conf.GetOpenedBoxes()
 	boxes := map[string]*Box{}
 	for _, box := range openedBoxes {
@@ -154,7 +163,13 @@ func SearchDocsByKeyword(keyword string) (ret []map[string]string) {
 	if "" != keyword {
 		for _, box := range boxes {
 			if strings.Contains(box.Name, keyword) {
-				ret = append(ret, map[string]string{"path": "/", "hPath": box.Name + "/", "box": box.ID, "boxIcon": box.Icon})
+				if flashcard {
+					if isBoxContainFlashcard(box.ID, deckBlockIDs) {
+						ret = append(ret, map[string]string{"path": "/", "hPath": box.Name + "/", "box": box.ID, "boxIcon": box.Icon})
+					}
+				} else {
+					ret = append(ret, map[string]string{"path": "/", "hPath": box.Name + "/", "box": box.ID, "boxIcon": box.Icon})
+				}
 			}
 		}
 
@@ -168,17 +183,29 @@ func SearchDocsByKeyword(keyword string) (ret []map[string]string) {
 		rootBlocks = sql.QueryRootBlockByCondition(condition)
 	} else {
 		for _, box := range boxes {
-			ret = append(ret, map[string]string{"path": "/", "hPath": box.Name + "/", "box": box.ID, "boxIcon": box.Icon})
+			if flashcard {
+				if isBoxContainFlashcard(box.ID, deckBlockIDs) {
+					ret = append(ret, map[string]string{"path": "/", "hPath": box.Name + "/", "box": box.ID, "boxIcon": box.Icon})
+				}
+			} else {
+				ret = append(ret, map[string]string{"path": "/", "hPath": box.Name + "/", "box": box.ID, "boxIcon": box.Icon})
+			}
 		}
 	}
 
-	for _, block := range rootBlocks {
-		b := boxes[block.Box]
+	for _, rootBlock := range rootBlocks {
+		b := boxes[rootBlock.Box]
 		if nil == b {
 			continue
 		}
-		hPath := b.Name + block.HPath
-		ret = append(ret, map[string]string{"path": block.Path, "hPath": hPath, "box": block.Box, "boxIcon": b.Icon})
+		hPath := b.Name + rootBlock.HPath
+		if flashcard {
+			if isTreeContainFlashcard(rootBlock.ID, deckBlockIDs) {
+				ret = append(ret, map[string]string{"path": rootBlock.Path, "hPath": hPath, "box": rootBlock.Box, "boxIcon": b.Icon})
+			}
+		} else {
+			ret = append(ret, map[string]string{"path": rootBlock.Path, "hPath": hPath, "box": rootBlock.Box, "boxIcon": b.Icon})
+		}
 	}
 
 	sort.Slice(ret, func(i, j int) bool {
@@ -194,7 +221,7 @@ type FileInfo struct {
 	isdir bool
 }
 
-func ListDocTree(boxID, path string, sortMode int) (ret []*File, totals int, err error) {
+func ListDocTree(boxID, path string, sortMode int, flashcard bool, maxListCount int) (ret []*File, totals int, err error) {
 	//os.MkdirAll("pprof", 0755)
 	//cpuProfile, _ := os.Create("pprof/cpu_profile_list_doc_tree")
 	//pprof.StartCPUProfile(cpuProfile)
@@ -202,6 +229,15 @@ func ListDocTree(boxID, path string, sortMode int) (ret []*File, totals int, err
 
 	ret = []*File{}
 
+	var deckBlockIDs []string
+	if flashcard {
+		deck := Decks[builtinDeckID]
+		if nil != deck {
+			return
+		}
+		deckBlockIDs = deck.GetBlockIDs()
+	}
+
 	box := Conf.Box(boxID)
 	if nil == box {
 		return nil, 0, errors.New(Conf.Language(0))
@@ -247,7 +283,15 @@ func ListDocTree(boxID, path string, sortMode int) (ret []*File, totals int, err
 						}
 					}
 				}
-				docs = append(docs, doc)
+
+				if flashcard {
+					rootID := strings.TrimSuffix(filepath.Base(parentDocPath), ".sy")
+					if isTreeContainFlashcard(rootID, deckBlockIDs) {
+						docs = append(docs, doc)
+					}
+				} else {
+					docs = append(docs, doc)
+				}
 			}
 			continue
 		}
@@ -259,8 +303,15 @@ func ListDocTree(boxID, path string, sortMode int) (ret []*File, totals int, err
 
 		if ial := box.docIAL(file.path); nil != ial {
 			doc := box.docFromFileInfo(file, ial)
-			docs = append(docs, doc)
-			continue
+
+			if flashcard {
+				rootID := strings.TrimSuffix(filepath.Base(file.path), ".sy")
+				if isTreeContainFlashcard(rootID, deckBlockIDs) {
+					docs = append(docs, doc)
+				}
+			} else {
+				docs = append(docs, doc)
+			}
 		}
 	}
 	elapsed = time.Now().Sub(start).Milliseconds()
@@ -312,8 +363,8 @@ func ListDocTree(boxID, path string, sortMode int) (ret []*File, totals int, err
 			return fileTreeFiles[i].Sort < fileTreeFiles[j].Sort
 		})
 		ret = append(ret, fileTreeFiles...)
-		if Conf.FileTree.MaxListCount < len(ret) {
-			ret = ret[:Conf.FileTree.MaxListCount]
+		if maxListCount < len(ret) {
+			ret = ret[:maxListCount]
 		}
 		ret = ret[:]
 		return
@@ -339,8 +390,8 @@ func ListDocTree(boxID, path string, sortMode int) (ret []*File, totals int, err
 		ret = append(ret, docs...)
 	}
 
-	if Conf.FileTree.MaxListCount < len(ret) {
-		ret = ret[:Conf.FileTree.MaxListCount]
+	if maxListCount < len(ret) {
+		ret = ret[:maxListCount]
 	}
 	ret = ret[:]
 

+ 56 - 8
kernel/model/flashcard.go

@@ -38,8 +38,60 @@ import (
 	"github.com/siyuan-note/siyuan/kernel/util"
 )
 
-var Decks = map[string]*riff.Deck{}
-var deckLock = sync.Mutex{}
+func GetFlashcardNotebooks() (ret []*Box) {
+	deck := Decks[builtinDeckID]
+	if nil == deck {
+		return
+	}
+	deckBlockIDs := deck.GetBlockIDs()
+
+	boxes := Conf.GetOpenedBoxes()
+	for _, box := range boxes {
+		if isBoxContainFlashcard(box.ID, deckBlockIDs) {
+			ret = append(ret, box)
+		}
+	}
+	return
+}
+
+func isTreeContainFlashcard(rootID string, deckBlockIDs []string) (ret bool) {
+	blockIDs := getTreeSubTreeChildBlocks(rootID)
+	for _, blockID := range deckBlockIDs {
+		if gulu.Str.Contains(blockID, blockIDs) {
+			return true
+		}
+	}
+	return
+}
+
+func isBoxContainFlashcard(boxID string, deckBlockIDs []string) (ret bool) {
+	entries, err := os.ReadDir(filepath.Join(util.DataDir, boxID))
+	if nil != err {
+		logging.LogErrorf("read dir failed: %s", err)
+		return
+	}
+
+	for _, entry := range entries {
+		if entry.IsDir() {
+			continue
+		}
+
+		if !strings.HasSuffix(entry.Name(), ".sy") {
+			continue
+		}
+
+		rootID := strings.TrimSuffix(entry.Name(), ".sy")
+		if isTreeContainFlashcard(rootID, deckBlockIDs) {
+			return true
+		}
+	}
+	return
+}
+
+var (
+	Decks    = map[string]*riff.Deck{}
+	deckLock = sync.Mutex{}
+)
 
 func GetNotebookFlashcards(boxID string, page int) (blocks []*Block, total, pageCount int) {
 	blocks = []*Block{}
@@ -66,9 +118,7 @@ func GetNotebookFlashcards(boxID string, page int) (blocks []*Block, total, page
 	var treeBlockIDs []string
 	for _, rootID := range rootIDs {
 		blockIDs := getTreeSubTreeChildBlocks(rootID)
-		for _, blockID := range blockIDs {
-			treeBlockIDs = append(treeBlockIDs, blockID)
-		}
+		treeBlockIDs = append(treeBlockIDs, blockIDs...)
 	}
 	treeBlockIDs = gulu.Str.RemoveDuplicatedElem(treeBlockIDs)
 
@@ -307,9 +357,7 @@ func GetNotebookDueFlashcards(boxID string, reviewedCardIDs []string) (ret []*Fl
 	var treeBlockIDs []string
 	for _, rootID := range rootIDs {
 		blockIDs := getTreeSubTreeChildBlocks(rootID)
-		for _, blockID := range blockIDs {
-			treeBlockIDs = append(treeBlockIDs, blockID)
-		}
+		treeBlockIDs = append(treeBlockIDs, blockIDs...)
 	}
 	treeBlockIDs = gulu.Str.RemoveDuplicatedElem(treeBlockIDs)
 

+ 1 - 1
kernel/model/mount.go

@@ -194,7 +194,7 @@ func Mount(boxID string) (alreadyMount bool, err error) {
 
 	box.Index()
 	// 缓存根一级的文档树展开
-	ListDocTree(box.ID, "/", Conf.FileTree.Sort)
+	ListDocTree(box.ID, "/", Conf.FileTree.Sort, false, Conf.FileTree.MaxListCount)
 	treenode.SaveBlockTree(false)
 	util.ClearPushProgress(100)
 

+ 2 - 2
kernel/treenode/heading.go

@@ -50,8 +50,8 @@ func MoveFoldHeading(updateNode, oldNode *ast.Node) {
 	})
 	for _, h := range updateFoldHeadings {
 		children := foldHeadings[h.ID]
-		for _, c := range children {
-			h.Next.InsertAfter(c) // Next 是 Block IAL
+		for i := len(children) - 1; 0 <= i; i-- {
+			h.Next.InsertAfter(children[i]) // Next 是 Block IAL
 		}
 	}
 	return