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

This commit is contained in:
Vanessa 2024-12-18 01:09:28 +08:00
commit f4f9610a90
7 changed files with 191 additions and 224 deletions

View File

@ -21,6 +21,8 @@
<a title="Twitter" target="_blank" href="https://twitter.com/b3logos"><img alt="Twitter Follow" src="https://img.shields.io/twitter/follow/b3logos?label=Follow&style=social"></a> <a title="Twitter" target="_blank" href="https://twitter.com/b3logos"><img alt="Twitter Follow" src="https://img.shields.io/twitter/follow/b3logos?label=Follow&style=social"></a>
<a title="Discord" target="_blank" href="https://discord.gg/dmMbCqVX7G"><img alt="Chat on Discord" src="https://img.shields.io/discord/808152298789666826?label=Discord&logo=Discord&style=social"></a> <a title="Discord" target="_blank" href="https://discord.gg/dmMbCqVX7G"><img alt="Chat on Discord" src="https://img.shields.io/discord/808152298789666826?label=Discord&logo=Discord&style=social"></a>
<br><br> <br><br>
<a href="https://trendshift.io/repositories/3949" target="_blank"><img src="https://trendshift.io/api/badge/repositories/3949" alt="siyuan-note%2Fsiyuan | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
<br><br>
<a href="https://www.producthunt.com/products/siyuan/reviews?utm_source=badge-product_rating&utm_medium=badge&utm_souce=badge-siyuan" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/product_rating.svg?product_id=534576&theme=light" alt="SiYuan - A&#0032;privacy&#0045;first&#0032;personal&#0032;knowledge&#0032;management&#0032;software | Product Hunt" style="width: 242px; height: 108px;" width="242" height="108" /></a> <a href="https://www.producthunt.com/products/siyuan/reviews?utm_source=badge-product_rating&utm_medium=badge&utm_souce=badge-siyuan" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/product_rating.svg?product_id=534576&theme=light" alt="SiYuan - A&#0032;privacy&#0045;first&#0032;personal&#0032;knowledge&#0032;management&#0032;software | Product Hunt" style="width: 242px; height: 108px;" width="242" height="108" /></a>
</p> </p>

View File

@ -21,6 +21,8 @@
<a title="Twitter" target="_blank" href="https://twitter.com/b3logos"><img alt="Twitter Follow" src="https://img.shields.io/twitter/follow/b3logos?label=Follow&style=social"></a> <a title="Twitter" target="_blank" href="https://twitter.com/b3logos"><img alt="Twitter Follow" src="https://img.shields.io/twitter/follow/b3logos?label=Follow&style=social"></a>
<a title="Discord" target="_blank" href="https://discord.gg/dmMbCqVX7G"><img alt="Chat on Discord" src="https://img.shields.io/discord/808152298789666826?label=Discord&logo=Discord&style=social"></a> <a title="Discord" target="_blank" href="https://discord.gg/dmMbCqVX7G"><img alt="Chat on Discord" src="https://img.shields.io/discord/808152298789666826?label=Discord&logo=Discord&style=social"></a>
<br><br> <br><br>
<a href="https://trendshift.io/repositories/3949" target="_blank"><img src="https://trendshift.io/api/badge/repositories/3949" alt="siyuan-note%2Fsiyuan | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
<br><br>
<a href="https://www.producthunt.com/products/siyuan/reviews?utm_source=badge-product_rating&utm_medium=badge&utm_souce=badge-siyuan" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/product_rating.svg?product_id=534576&theme=light" alt="SiYuan - A&#0032;privacy&#0045;first&#0032;personal&#0032;knowledge&#0032;management&#0032;software | Product Hunt" style="width: 242px; height: 108px;" width="242" height="108" /></a> <a href="https://www.producthunt.com/products/siyuan/reviews?utm_source=badge-product_rating&utm_medium=badge&utm_souce=badge-siyuan" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/product_rating.svg?product_id=534576&theme=light" alt="SiYuan - A&#0032;privacy&#0045;first&#0032;personal&#0032;knowledge&#0032;management&#0032;software | Product Hunt" style="width: 242px; height: 108px;" width="242" height="108" /></a>
</p> </p>

View File

@ -21,6 +21,8 @@
<a title="Twitter" target="_blank" href="https://twitter.com/b3logos"><img alt="Twitter Follow" src="https://img.shields.io/twitter/follow/b3logos?label=Follow&style=social"></a> <a title="Twitter" target="_blank" href="https://twitter.com/b3logos"><img alt="Twitter Follow" src="https://img.shields.io/twitter/follow/b3logos?label=Follow&style=social"></a>
<a title="Discord" target="_blank" href="https://discord.gg/dmMbCqVX7G"><img alt="Chat on Discord" src="https://img.shields.io/discord/808152298789666826?label=Discord&logo=Discord&style=social"></a> <a title="Discord" target="_blank" href="https://discord.gg/dmMbCqVX7G"><img alt="Chat on Discord" src="https://img.shields.io/discord/808152298789666826?label=Discord&logo=Discord&style=social"></a>
<br><br> <br><br>
<a href="https://trendshift.io/repositories/3949" target="_blank"><img src="https://trendshift.io/api/badge/repositories/3949" alt="siyuan-note%2Fsiyuan | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
<br><br>
<a href="https://www.producthunt.com/products/siyuan/reviews?utm_source=badge-product_rating&utm_medium=badge&utm_souce=badge-siyuan" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/product_rating.svg?product_id=534576&theme=light" alt="SiYuan - A&#0032;privacy&#0045;first&#0032;personal&#0032;knowledge&#0032;management&#0032;software | Product Hunt" style="width: 242px; height: 108px;" width="242" height="108" /></a> <a href="https://www.producthunt.com/products/siyuan/reviews?utm_source=badge-product_rating&utm_medium=badge&utm_souce=badge-siyuan" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/product_rating.svg?product_id=534576&theme=light" alt="SiYuan - A&#0032;privacy&#0045;first&#0032;personal&#0032;knowledge&#0032;management&#0032;software | Product Hunt" style="width: 242px; height: 108px;" width="242" height="108" /></a>
</p> </p>

View File

@ -392,7 +392,7 @@ export const repos = {
<option value="3" ${window.siyuan.config.sync.mode === 3 ? "selected" : ""}>${window.siyuan.languages.syncMode3}</option> <option value="3" ${window.siyuan.config.sync.mode === 3 ? "selected" : ""}>${window.siyuan.languages.syncMode3}</option>
</select> </select>
</div> </div>
<label class="fn__flex b3-label${(window.siyuan.config.sync.mode !== 1 || window.siyuan.config.system.container === "docker" || window.siyuan.config.sync.provider !== 0) ? " fn__none" : ""}"> <label class="fn__flex b3-label${(window.siyuan.config.sync.mode !== 1) ? " fn__none" : ""}">
<div class="fn__flex-1"> <div class="fn__flex-1">
${window.siyuan.languages.syncInterval} ${window.siyuan.languages.syncInterval}
<div class="b3-label__text">${window.siyuan.languages.syncIntervalTip}</div> <div class="b3-label__text">${window.siyuan.languages.syncIntervalTip}</div>
@ -477,9 +477,12 @@ export const repos = {
fetchPost("/api/sync/setSyncMode", {mode: parseInt(syncModeElement.value, 10)}, () => { fetchPost("/api/sync/setSyncMode", {mode: parseInt(syncModeElement.value, 10)}, () => {
if (syncModeElement.value === "1" && window.siyuan.config.sync.provider === 0 && window.siyuan.config.system.container !== "docker") { if (syncModeElement.value === "1" && window.siyuan.config.sync.provider === 0 && window.siyuan.config.system.container !== "docker") {
syncPerceptionElement.parentElement.classList.remove("fn__none"); syncPerceptionElement.parentElement.classList.remove("fn__none");
syncIntervalElement.parentElement.classList.remove("fn__none");
} else { } else {
syncPerceptionElement.parentElement.classList.add("fn__none"); syncPerceptionElement.parentElement.classList.add("fn__none");
}
if (syncModeElement.value === "1") {
syncIntervalElement.parentElement.classList.remove("fn__none");
} else {
syncIntervalElement.parentElement.classList.add("fn__none"); syncIntervalElement.parentElement.classList.add("fn__none");
} }
window.siyuan.config.sync.mode = parseInt(syncModeElement.value, 10); window.siyuan.config.sync.mode = parseInt(syncModeElement.value, 10);
@ -502,9 +505,12 @@ export const repos = {
syncConfigElement.classList.add("fn__none"); syncConfigElement.classList.add("fn__none");
if (window.siyuan.config.sync.mode !== 1 || window.siyuan.config.system.container === "docker" || window.siyuan.config.sync.provider !== 0) { if (window.siyuan.config.sync.mode !== 1 || window.siyuan.config.system.container === "docker" || window.siyuan.config.sync.provider !== 0) {
syncPerceptionElement.parentElement.classList.add("fn__none"); syncPerceptionElement.parentElement.classList.add("fn__none");
syncIntervalElement.parentElement.classList.add("fn__none");
} else { } else {
syncPerceptionElement.parentElement.classList.remove("fn__none"); syncPerceptionElement.parentElement.classList.remove("fn__none");
}
if (window.siyuan.config.sync.mode !== 1) {
syncIntervalElement.parentElement.classList.add("fn__none");
} else {
syncIntervalElement.parentElement.classList.remove("fn__none"); syncIntervalElement.parentElement.classList.remove("fn__none");
} }
}); });

View File

@ -243,7 +243,7 @@ export const setInlineStyle = (set = true) => {
unicode-range: U+263a, U+2194-2199, U+2934-2935, U+2639, U+26a0, U+25b6, U+25c0, U+23cf, U+2640, U+2642, U+203c, U+2049, unicode-range: U+263a, U+2194-2199, U+2934-2935, U+2639, U+26a0, U+25b6, U+25c0, U+23cf, U+2640, U+2642, U+203c, U+2049,
U+2611, U+303d, U+00a9, U+00ae, U+2122, U+1f170-1f171, U+24c2, U+1f17e, U+1f17f, U+1f22f, U+1f250, U+1f21a, U+2611, U+303d, U+00a9, U+00ae, U+2122, U+1f170-1f171, U+24c2, U+1f17e, U+1f17f, U+1f22f, U+1f250, U+1f21a,
U+1f232-1f23a, U+1f251, U+3297, U+3299, U+25aa, U+25ab, U+2660, U+2666, U+2665, U+2663, U+1f636, U+1f62e, U+1f642, U+1f232-1f23a, U+1f251, U+3297, U+3299, U+25aa, U+25ab, U+2660, U+2666, U+2665, U+2663, U+1f636, U+1f62e, U+1f642,
U+1f635, U+2620, U+2763, U+2764, U+1f441, U+270c, U+261d, U+270d, U+200d, U+e50a, U+3030; U+1f635, U+2620, U+2763, U+2764, U+1f441, U+270c, U+261d, U+270d, U+200d, U+e50a, U+3030, U+21aa, U+21a9;
}`; }`;
} }
style += `.b3-typography, .protyle-wysiwyg, .protyle-title {font-size:${window.siyuan.config.editor.fontSize}px !important} style += `.b3-typography, .protyle-wysiwyg, .protyle-title {font-size:${window.siyuan.config.editor.fontSize}px !important}

View File

@ -67,7 +67,7 @@ func exportEPUB(c *gin.Context) {
} }
id := arg["id"].(string) id := arg["id"].(string)
name, zipPath := model.ExportPandocConvertZip(id, "epub", ".epub") name, zipPath := model.ExportPandocConvertZip([]string{id}, "epub", ".epub")
ret.Data = map[string]interface{}{ ret.Data = map[string]interface{}{
"name": name, "name": name,
"zip": zipPath, "zip": zipPath,
@ -84,7 +84,7 @@ func exportRTF(c *gin.Context) {
} }
id := arg["id"].(string) id := arg["id"].(string)
name, zipPath := model.ExportPandocConvertZip(id, "rtf", ".rtf") name, zipPath := model.ExportPandocConvertZip([]string{id}, "rtf", ".rtf")
ret.Data = map[string]interface{}{ ret.Data = map[string]interface{}{
"name": name, "name": name,
"zip": zipPath, "zip": zipPath,
@ -101,7 +101,7 @@ func exportODT(c *gin.Context) {
} }
id := arg["id"].(string) id := arg["id"].(string)
name, zipPath := model.ExportPandocConvertZip(id, "odt", ".odt") name, zipPath := model.ExportPandocConvertZip([]string{id}, "odt", ".odt")
ret.Data = map[string]interface{}{ ret.Data = map[string]interface{}{
"name": name, "name": name,
"zip": zipPath, "zip": zipPath,
@ -118,7 +118,7 @@ func exportMediaWiki(c *gin.Context) {
} }
id := arg["id"].(string) id := arg["id"].(string)
name, zipPath := model.ExportPandocConvertZip(id, "mediawiki", ".wiki") name, zipPath := model.ExportPandocConvertZip([]string{id}, "mediawiki", ".wiki")
ret.Data = map[string]interface{}{ ret.Data = map[string]interface{}{
"name": name, "name": name,
"zip": zipPath, "zip": zipPath,
@ -135,7 +135,7 @@ func exportOrgMode(c *gin.Context) {
} }
id := arg["id"].(string) id := arg["id"].(string)
name, zipPath := model.ExportPandocConvertZip(id, "org", ".org") name, zipPath := model.ExportPandocConvertZip([]string{id}, "org", ".org")
ret.Data = map[string]interface{}{ ret.Data = map[string]interface{}{
"name": name, "name": name,
"zip": zipPath, "zip": zipPath,
@ -152,7 +152,7 @@ func exportOPML(c *gin.Context) {
} }
id := arg["id"].(string) id := arg["id"].(string)
name, zipPath := model.ExportPandocConvertZip(id, "opml", ".opml") name, zipPath := model.ExportPandocConvertZip([]string{id}, "opml", ".opml")
ret.Data = map[string]interface{}{ ret.Data = map[string]interface{}{
"name": name, "name": name,
"zip": zipPath, "zip": zipPath,
@ -169,7 +169,7 @@ func exportTextile(c *gin.Context) {
} }
id := arg["id"].(string) id := arg["id"].(string)
name, zipPath := model.ExportPandocConvertZip(id, "textile", ".textile") name, zipPath := model.ExportPandocConvertZip([]string{id}, "textile", ".textile")
ret.Data = map[string]interface{}{ ret.Data = map[string]interface{}{
"name": name, "name": name,
"zip": zipPath, "zip": zipPath,
@ -186,7 +186,7 @@ func exportAsciiDoc(c *gin.Context) {
} }
id := arg["id"].(string) id := arg["id"].(string)
name, zipPath := model.ExportPandocConvertZip(id, "asciidoc", ".adoc") name, zipPath := model.ExportPandocConvertZip([]string{id}, "asciidoc", ".adoc")
ret.Data = map[string]interface{}{ ret.Data = map[string]interface{}{
"name": name, "name": name,
"zip": zipPath, "zip": zipPath,
@ -203,7 +203,7 @@ func exportReStructuredText(c *gin.Context) {
} }
id := arg["id"].(string) id := arg["id"].(string)
name, zipPath := model.ExportPandocConvertZip(id, "rst", ".rst") name, zipPath := model.ExportPandocConvertZip([]string{id}, "rst", ".rst")
ret.Data = map[string]interface{}{ ret.Data = map[string]interface{}{
"name": name, "name": name,
"zip": zipPath, "zip": zipPath,
@ -340,7 +340,7 @@ func exportMds(c *gin.Context) {
ids = append(ids, id.(string)) ids = append(ids, id.(string))
} }
name, zipPath := model.BatchExportPandocConvertZip(ids, "", ".md") name, zipPath := model.ExportPandocConvertZip(ids, "", ".md")
ret.Data = map[string]interface{}{ ret.Data = map[string]interface{}{
"name": name, "name": name,
"zip": zipPath, "zip": zipPath,
@ -357,7 +357,7 @@ func exportMd(c *gin.Context) {
} }
id := arg["id"].(string) id := arg["id"].(string)
name, zipPath := model.ExportPandocConvertZip(id, "", ".md") name, zipPath := model.ExportPandocConvertZip([]string{id}, "", ".md")
ret.Data = map[string]interface{}{ ret.Data = map[string]interface{}{
"name": name, "name": name,
"zip": zipPath, "zip": zipPath,

View File

@ -269,10 +269,10 @@ func Export2Liandi(id string) (err error) {
title := path.Base(tree.HPath) title := path.Base(tree.HPath)
tags := tree.Root.IALAttr("tags") tags := tree.Root.IALAttr("tags")
content := exportMarkdownContent0(tree, util.GetCloudForumAssetsServer()+time.Now().Format("2006/01")+"/siyuan/"+Conf.GetUser().UserId+"/", true, content := exportMarkdownContent0(tree, util.GetCloudForumAssetsServer()+time.Now().Format("2006/01")+"/siyuan/"+Conf.GetUser().UserId+"/", true,
4, 1, 0, ".md", 4, 1, 0,
"#", "#", "#", "#",
"", "", "", "",
false, nil) false, nil, true)
result := gulu.Ret.NewResult() result := gulu.Ret.NewResult()
request := httpclient.NewCloudRequest30s() request := httpclient.NewCloudRequest30s()
request = request. request = request.
@ -578,7 +578,7 @@ func Preview(id string) (retStdHTML string) {
blockRefMode, Conf.Export.BlockEmbedMode, Conf.Export.FileAnnotationRefMode, blockRefMode, Conf.Export.BlockEmbedMode, Conf.Export.FileAnnotationRefMode,
Conf.Export.TagOpenMarker, Conf.Export.TagCloseMarker, Conf.Export.TagOpenMarker, Conf.Export.TagCloseMarker,
Conf.Export.BlockRefTextLeft, Conf.Export.BlockRefTextRight, Conf.Export.BlockRefTextLeft, Conf.Export.BlockRefTextRight,
Conf.Export.AddTitle, true) Conf.Export.AddTitle, true, true)
luteEngine := NewLute() luteEngine := NewLute()
luteEngine.SetFootnotes(true) luteEngine.SetFootnotes(true)
addBlockIALNodes(tree, false) addBlockIALNodes(tree, false)
@ -681,7 +681,7 @@ func ExportMarkdownHTML(id, savePath string, docx, merge bool) (name, dom string
blockRefMode, Conf.Export.BlockEmbedMode, Conf.Export.FileAnnotationRefMode, blockRefMode, Conf.Export.BlockEmbedMode, Conf.Export.FileAnnotationRefMode,
Conf.Export.TagOpenMarker, Conf.Export.TagCloseMarker, Conf.Export.TagOpenMarker, Conf.Export.TagCloseMarker,
Conf.Export.BlockRefTextLeft, Conf.Export.BlockRefTextRight, Conf.Export.BlockRefTextLeft, Conf.Export.BlockRefTextRight,
Conf.Export.AddTitle, true) Conf.Export.AddTitle, true, true)
name = path.Base(tree.HPath) name = path.Base(tree.HPath)
name = util.FilterFileName(name) // 导出 PDF、HTML 和 Word 时未移除不支持的文件名符号 https://github.com/siyuan-note/siyuan/issues/5614 name = util.FilterFileName(name) // 导出 PDF、HTML 和 Word 时未移除不支持的文件名符号 https://github.com/siyuan-note/siyuan/issues/5614
savePath = strings.TrimSpace(savePath) savePath = strings.TrimSpace(savePath)
@ -840,7 +840,7 @@ func ExportHTML(id, savePath string, pdf, image, keepFold, merge bool) (name, do
blockRefMode, Conf.Export.BlockEmbedMode, Conf.Export.FileAnnotationRefMode, blockRefMode, Conf.Export.BlockEmbedMode, Conf.Export.FileAnnotationRefMode,
Conf.Export.TagOpenMarker, Conf.Export.TagCloseMarker, Conf.Export.TagOpenMarker, Conf.Export.TagCloseMarker,
Conf.Export.BlockRefTextLeft, Conf.Export.BlockRefTextRight, Conf.Export.BlockRefTextLeft, Conf.Export.BlockRefTextRight,
Conf.Export.AddTitle, true) Conf.Export.AddTitle, true, true)
name = path.Base(tree.HPath) name = path.Base(tree.HPath)
name = util.FilterFileName(name) // 导出 PDF、HTML 和 Word 时未移除不支持的文件名符号 https://github.com/siyuan-note/siyuan/issues/5614 name = util.FilterFileName(name) // 导出 PDF、HTML 和 Word 时未移除不支持的文件名符号 https://github.com/siyuan-note/siyuan/issues/5614
@ -1421,7 +1421,6 @@ func ExportStdMarkdown(id string) string {
var defID string var defID string
if treenode.IsBlockLink(n) { if treenode.IsBlockLink(n) {
defID = strings.TrimPrefix(n.TextMarkAHref, "siyuan://blocks/") defID = strings.TrimPrefix(n.TextMarkAHref, "siyuan://blocks/")
} else if treenode.IsBlockRef(n) { } else if treenode.IsBlockRef(n) {
defID, _, _ = treenode.GetBlockRef(n) defID, _, _ = treenode.GetBlockRef(n)
} }
@ -1438,13 +1437,13 @@ func ExportStdMarkdown(id string) string {
defBlockIDs = gulu.Str.RemoveDuplicatedElem(defBlockIDs) defBlockIDs = gulu.Str.RemoveDuplicatedElem(defBlockIDs)
return exportMarkdownContent0(tree, cloudAssetsBase, false, return exportMarkdownContent0(tree, cloudAssetsBase, false,
Conf.Export.BlockRefMode, Conf.Export.BlockEmbedMode, Conf.Export.FileAnnotationRefMode, ".md", Conf.Export.BlockRefMode, Conf.Export.BlockEmbedMode, Conf.Export.FileAnnotationRefMode,
Conf.Export.TagOpenMarker, Conf.Export.TagCloseMarker, Conf.Export.TagOpenMarker, Conf.Export.TagCloseMarker,
Conf.Export.BlockRefTextLeft, Conf.Export.BlockRefTextRight, Conf.Export.BlockRefTextLeft, Conf.Export.BlockRefTextRight,
Conf.Export.AddTitle, defBlockIDs) Conf.Export.AddTitle, defBlockIDs, true)
} }
func BatchExportPandocConvertZip(ids []string, pandocTo, ext string) (name, zipPath string) { func ExportPandocConvertZip(ids []string, pandocTo, ext string) (name, zipPath string) {
block := treenode.GetBlockTree(ids[0]) block := treenode.GetBlockTree(ids[0])
box := Conf.Box(block.BoxID) box := Conf.Box(block.BoxID)
baseFolderName := path.Base(block.HPath) baseFolderName := path.Base(block.HPath)
@ -1463,31 +1462,8 @@ func BatchExportPandocConvertZip(ids []string, pandocTo, ext string) (name, zipP
} }
docPaths = util.FilterSelfChildDocs(docPaths) docPaths = util.FilterSelfChildDocs(docPaths)
zipPath = exportPandocConvertZip(baseFolderName, docPaths, "gfm+footnotes+hard_line_breaks", pandocTo, ext) defBlockIDs, trees, docPaths := prepareExportTrees(docPaths)
name = util.GetTreeID(block.Path) zipPath = exportPandocConvertZip(baseFolderName, docPaths, defBlockIDs, "gfm+footnotes+hard_line_breaks", pandocTo, ext, trees)
return
}
func ExportPandocConvertZip(id, pandocTo, ext string) (name, zipPath string) {
block := treenode.GetBlockTree(id)
if nil == block {
logging.LogErrorf("not found block [%s]", id)
return
}
boxID := block.BoxID
box := Conf.Box(boxID)
baseFolderName := path.Base(block.HPath)
if "." == baseFolderName {
baseFolderName = path.Base(block.Path)
}
docPaths := []string{block.Path}
docFiles := box.ListFiles(strings.TrimSuffix(block.Path, ".sy"))
for _, docFile := range docFiles {
docPaths = append(docPaths, docFile.path)
}
zipPath = exportPandocConvertZip(baseFolderName, docPaths, "gfm+footnotes+hard_line_breaks", pandocTo, ext)
name = util.GetTreeID(block.Path) name = util.GetTreeID(block.Path)
return return
} }
@ -1515,7 +1491,9 @@ func ExportNotebookMarkdown(boxID, folderPath string) (zipPath string) {
for _, docFile := range docFiles { for _, docFile := range docFiles {
docPaths = append(docPaths, docFile.path) docPaths = append(docPaths, docFile.path)
} }
zipPath = exportPandocConvertZip(baseFolderName, docPaths, "", "", ".md")
defBlockIDs, trees, docPaths := prepareExportTrees(docPaths)
zipPath = exportPandocConvertZip(baseFolderName, docPaths, defBlockIDs, "", "", ".md", trees)
return return
} }
@ -1642,7 +1620,7 @@ func exportSYZip(boxID, rootDirPath, baseFolderName string, docPaths []string) (
util.PushEndlessProgress(Conf.language(65) + " " + fmt.Sprintf(Conf.language(70), fmt.Sprintf("%d/%d %s", count, len(docPaths), tree.Root.IALAttr("title")))) util.PushEndlessProgress(Conf.language(65) + " " + fmt.Sprintf(Conf.language(70), fmt.Sprintf("%d/%d %s", count, len(docPaths), tree.Root.IALAttr("title"))))
refs := map[string]*parse.Tree{} refs := map[string]*parse.Tree{}
exportRefTrees(tree, &refs, &treeCache) exportRefTrees(tree, &[]string{}, &refs, &treeCache)
for refTreeID, refTree := range refs { for refTreeID, refTree := range refs {
if nil == trees[refTreeID] { if nil == trees[refTreeID] {
refTrees[refTreeID] = refTree refTrees[refTreeID] = refTree
@ -1949,10 +1927,10 @@ func walkRelationAvs(avID string, exportAvIDs *hashset.Set) {
} }
func ExportMarkdownContent(id string) (hPath, exportedMd string) { func ExportMarkdownContent(id string) (hPath, exportedMd string) {
return exportMarkdownContent(id, Conf.Export.BlockRefMode, nil) return exportMarkdownContent(id, ".md", Conf.Export.BlockRefMode, nil, true)
} }
func exportMarkdownContent(id string, exportRefMode int, defBlockIDs []string) (hPath, exportedMd string) { func exportMarkdownContent(id, ext string, exportRefMode int, defBlockIDs []string, singleFile bool) (hPath, exportedMd string) {
tree, err := LoadTreeByBlockID(id) tree, err := LoadTreeByBlockID(id)
if err != nil { if err != nil {
logging.LogErrorf("load tree by block id [%s] failed: %s", id, err) logging.LogErrorf("load tree by block id [%s] failed: %s", id, err)
@ -1960,26 +1938,24 @@ func exportMarkdownContent(id string, exportRefMode int, defBlockIDs []string) (
} }
hPath = tree.HPath hPath = tree.HPath
exportedMd = exportMarkdownContent0(tree, "", false, exportedMd = exportMarkdownContent0(tree, "", false,
exportRefMode, Conf.Export.BlockEmbedMode, Conf.Export.FileAnnotationRefMode, ext, exportRefMode, Conf.Export.BlockEmbedMode, Conf.Export.FileAnnotationRefMode,
Conf.Export.TagOpenMarker, Conf.Export.TagCloseMarker, Conf.Export.TagOpenMarker, Conf.Export.TagCloseMarker,
Conf.Export.BlockRefTextLeft, Conf.Export.BlockRefTextRight, Conf.Export.BlockRefTextLeft, Conf.Export.BlockRefTextRight,
Conf.Export.AddTitle, defBlockIDs) Conf.Export.AddTitle, defBlockIDs, singleFile)
docIAL := parse.IAL2Map(tree.Root.KramdownIAL) docIAL := parse.IAL2Map(tree.Root.KramdownIAL)
exportedMd = yfm(docIAL) + exportedMd exportedMd = yfm(docIAL) + exportedMd
return return
} }
func exportMarkdownContent0(tree *parse.Tree, cloudAssetsBase string, assetsDestSpace2Underscore bool, func exportMarkdownContent0(tree *parse.Tree, cloudAssetsBase string, assetsDestSpace2Underscore bool,
blockRefMode, blockEmbedMode, fileAnnotationRefMode int, ext string, blockRefMode, blockEmbedMode, fileAnnotationRefMode int,
tagOpenMarker, tagCloseMarker string, tagOpenMarker, tagCloseMarker string, blockRefTextLeft, blockRefTextRight string,
blockRefTextLeft, blockRefTextRight string, addTitle bool, defBlockIDs []string, singleFile bool) (ret string) {
addTitle bool,
defBlockIDs []string) (ret string) {
tree = exportTree(tree, false, false, false, tree = exportTree(tree, false, false, false,
blockRefMode, blockEmbedMode, fileAnnotationRefMode, blockRefMode, blockEmbedMode, fileAnnotationRefMode,
tagOpenMarker, tagCloseMarker, tagOpenMarker, tagCloseMarker,
blockRefTextLeft, blockRefTextRight, blockRefTextLeft, blockRefTextRight,
addTitle, 0 < len(defBlockIDs)) addTitle, 0 < len(defBlockIDs), singleFile)
luteEngine := NewLute() luteEngine := NewLute()
luteEngine.SetFootnotes(true) luteEngine.SetFootnotes(true)
luteEngine.SetKramdownIAL(false) luteEngine.SetKramdownIAL(false)
@ -2057,7 +2033,7 @@ func exportMarkdownContent0(tree *parse.Tree, cloudAssetsBase string, assetsDest
var href string var href string
bt := treenode.GetBlockTree(defID) bt := treenode.GetBlockTree(defID)
if nil != bt { if nil != bt {
href += bt.HPath + ".md" href += bt.HPath + ext
if "d" != bt.Type { if "d" != bt.Type {
href += "#" + defID href += "#" + defID
} }
@ -2090,7 +2066,7 @@ func exportTree(tree *parse.Tree, wysiwyg, keepFold, avHiddenCol bool,
blockRefMode, blockEmbedMode, fileAnnotationRefMode int, blockRefMode, blockEmbedMode, fileAnnotationRefMode int,
tagOpenMarker, tagCloseMarker string, tagOpenMarker, tagCloseMarker string,
blockRefTextLeft, blockRefTextRight string, blockRefTextLeft, blockRefTextRight string,
addTitle, addDocAnchorSpan bool) (ret *parse.Tree) { addTitle, addDocAnchorSpan, singleFile bool) (ret *parse.Tree) {
luteEngine := NewLute() luteEngine := NewLute()
ret = tree ret = tree
id := tree.Root.ID id := tree.Root.ID
@ -2107,7 +2083,7 @@ func exportTree(tree *parse.Tree, wysiwyg, keepFold, avHiddenCol bool,
// 收集引用转脚注+锚点哈希 // 收集引用转脚注+锚点哈希
var refFootnotes []*refAsFootnotes var refFootnotes []*refAsFootnotes
if 4 == blockRefMode { if 4 == blockRefMode && singleFile {
depth = 0 depth = 0
collectFootnotesDefs(ret, ret.ID, &refFootnotes, &treeCache, &depth) collectFootnotesDefs(ret, ret.ID, &refFootnotes, &treeCache, &depth)
} }
@ -2266,10 +2242,12 @@ func exportTree(tree *parse.Tree, wysiwyg, keepFold, avHiddenCol bool,
} }
} else { } else {
if 4 == blockRefMode { // 脚注+锚点哈希 if 4 == blockRefMode { // 脚注+锚点哈希
if addDocAnchorSpan {
anchorSpan := treenode.NewSpanAnchor(id) anchorSpan := treenode.NewSpanAnchor(id)
ret.Root.PrependChild(anchorSpan) ret.Root.PrependChild(anchorSpan)
} }
} }
}
// 导出时支持导出题头图 https://github.com/siyuan-note/siyuan/issues/4372 // 导出时支持导出题头图 https://github.com/siyuan-note/siyuan/issues/4372
titleImgPath := treenode.GetDocTitleImgPath(ret.Root) titleImgPath := treenode.GetDocTitleImgPath(ret.Root)
@ -2965,84 +2943,6 @@ type refAsFootnotes struct {
refAnchorText string refAnchorText string
} }
func exportRefTrees(tree *parse.Tree, retTrees, treeCache *map[string]*parse.Tree) {
if nil != (*retTrees)[tree.ID] {
return
}
(*retTrees)[tree.ID] = tree
ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
if !entering {
return ast.WalkContinue
}
if treenode.IsBlockRef(n) {
defID, _, _ := treenode.GetBlockRef(n)
if "" == defID {
return ast.WalkContinue
}
defBlock := treenode.GetBlockTree(defID)
if nil == defBlock {
return ast.WalkSkipChildren
}
var defTree *parse.Tree
var err error
if (*treeCache)[defBlock.RootID] != nil {
defTree = (*treeCache)[defBlock.RootID]
} else {
defTree, err = LoadTreeByBlockID(defBlock.RootID)
if err != nil {
return ast.WalkSkipChildren
}
(*treeCache)[defBlock.RootID] = defTree
}
exportRefTrees(defTree, retTrees, treeCache)
} else if ast.NodeAttributeView == n.Type {
// 导出数据库所在文档时一并导出绑定块所在文档
// Export the binding block docs when exporting the doc where the database is located https://github.com/siyuan-note/siyuan/issues/11486
avID := n.AttributeViewID
if "" == avID {
return ast.WalkContinue
}
attrView, _ := av.ParseAttributeView(avID)
if nil == attrView {
return ast.WalkContinue
}
blockKeyValues := attrView.GetBlockKeyValues()
if nil == blockKeyValues {
return ast.WalkContinue
}
for _, val := range blockKeyValues.Values {
defBlock := treenode.GetBlockTree(val.BlockID)
if nil == defBlock {
continue
}
var defTree *parse.Tree
var err error
if (*treeCache)[defBlock.RootID] != nil {
defTree = (*treeCache)[defBlock.RootID]
} else {
defTree, err = LoadTreeByBlockID(defBlock.RootID)
if err != nil {
continue
}
(*treeCache)[defBlock.RootID] = defTree
}
exportRefTrees(defTree, retTrees, treeCache)
}
}
return ast.WalkContinue
})
}
func processFileAnnotationRef(refID string, n *ast.Node, fileAnnotationRefMode int) ast.WalkStatus { func processFileAnnotationRef(refID string, n *ast.Node, fileAnnotationRefMode int) ast.WalkStatus {
p := refID[:strings.LastIndex(refID, "/")] p := refID[:strings.LastIndex(refID, "/")]
absPath, err := GetAssetAbsPath(p) absPath, err := GetAssetAbsPath(p)
@ -3090,8 +2990,8 @@ func processFileAnnotationRef(refID string, n *ast.Node, fileAnnotationRefMode i
return ast.WalkSkipChildren return ast.WalkSkipChildren
} }
func exportPandocConvertZip(baseFolderName string, docPaths []string, func exportPandocConvertZip(baseFolderName string, docPaths, defBlockIDs []string,
pandocFrom, pandocTo, ext string) (zipPath string) { pandocFrom, pandocTo, ext string, treeCache *map[string]*parse.Tree) (zipPath string) {
defer util.ClearPushProgress(100) defer util.ClearPushProgress(100)
dir, name := path.Split(baseFolderName) dir, name := path.Split(baseFolderName)
@ -3111,49 +3011,6 @@ func exportPandocConvertZip(baseFolderName string, docPaths []string,
} }
exportRefMode := Conf.Export.BlockRefMode exportRefMode := Conf.Export.BlockRefMode
var defBlockIDs []string
if 4 == exportRefMode { // 脚注+锚点哈希
// 导出锚点哈希,这里先记录下所有定义块的 ID
walked := map[string]bool{}
for _, p := range docPaths {
if walked[p] {
continue
}
id := util.GetTreeID(p)
tree, err := LoadTreeByBlockID(id)
if err != nil {
continue
}
ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
if !entering {
return ast.WalkContinue
}
var defID string
if treenode.IsBlockLink(n) {
defID = strings.TrimPrefix(n.TextMarkAHref, "siyuan://blocks/")
} else if treenode.IsBlockRef(n) {
defID, _, _ = treenode.GetBlockRef(n)
}
if "" != defID {
if defBt := treenode.GetBlockTree(defID); nil != defBt {
docPaths = append(docPaths, defBt.Path)
docPaths = gulu.Str.RemoveDuplicatedElem(docPaths)
defBlockIDs = append(defBlockIDs, defID)
defBlockIDs = gulu.Str.RemoveDuplicatedElem(defBlockIDs)
walked[defBt.Path] = true
}
}
return ast.WalkContinue
})
}
defBlockIDs = gulu.Str.RemoveDuplicatedElem(defBlockIDs)
docPaths = gulu.Str.RemoveDuplicatedElem(docPaths)
}
wrotePathHash := map[string]string{} wrotePathHash := map[string]string{}
assetsPathMap, err := allAssetAbsPaths() assetsPathMap, err := allAssetAbsPaths()
if nil != err { if nil != err {
@ -3164,7 +3021,7 @@ func exportPandocConvertZip(baseFolderName string, docPaths []string,
luteEngine := util.NewLute() luteEngine := util.NewLute()
for i, p := range docPaths { for i, p := range docPaths {
id := util.GetTreeID(p) id := util.GetTreeID(p)
hPath, md := exportMarkdownContent(id, exportRefMode, defBlockIDs) hPath, md := exportMarkdownContent(id, ext, exportRefMode, defBlockIDs, false)
dir, name = path.Split(hPath) dir, name = path.Split(hPath)
dir = util.FilterFilePath(dir) // 导出文档时未移除不支持的文件名符号 https://github.com/siyuan-note/siyuan/issues/4590 dir = util.FilterFilePath(dir) // 导出文档时未移除不支持的文件名符号 https://github.com/siyuan-note/siyuan/issues/4590
name = util.FilterFileName(name) name = util.FilterFileName(name)
@ -3269,41 +3126,139 @@ func getExportBlockRefLinkText(blockRef *ast.Node, blockRefTextLeft, blockRefTex
return return
} }
func getDestViewVal(attrView *av.AttributeView, keyID, blockID string) *av.TableColumn { func prepareExportTrees(docPaths []string) (defBlockIDs []string, trees *map[string]*parse.Tree, relatedDocPaths []string) {
rollupKey, _ := attrView.GetKey(keyID) trees = &map[string]*parse.Tree{}
if nil == rollupKey || nil == rollupKey.Rollup { treeCache := &map[string]*parse.Tree{}
return nil defBlockIDs = []string{}
for _, p := range docPaths {
id := util.GetTreeID(p)
tree, err := LoadTreeByBlockID(id)
if err != nil {
continue
}
exportRefTrees(tree, &defBlockIDs, trees, treeCache)
} }
relKey, _ := attrView.GetKey(rollupKey.Rollup.RelationKeyID) for _, tree := range *trees {
if nil == relKey || nil == relKey.Relation { relatedDocPaths = append(relatedDocPaths, tree.Path)
return nil }
relatedDocPaths = gulu.Str.RemoveDuplicatedElem(relatedDocPaths)
return
} }
relVal := attrView.GetValue(relKey.ID, blockID) func exportRefTrees(tree *parse.Tree, defBlockIDs *[]string, retTrees, treeCache *map[string]*parse.Tree) {
if nil == relVal || nil == relVal.Relation { if nil != (*retTrees)[tree.ID] {
return nil return
}
(*retTrees)[tree.ID] = tree
ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
if !entering {
return ast.WalkContinue
} }
destAv, _ := av.ParseAttributeView(relKey.Relation.AvID) if treenode.IsBlockRef(n) {
if nil == destAv { defID, _, _ := treenode.GetBlockRef(n)
return nil if "" == defID {
return ast.WalkContinue
}
defBlock := treenode.GetBlockTree(defID)
if nil == defBlock {
return ast.WalkSkipChildren
} }
destKey, _ := destAv.GetKey(rollupKey.Rollup.KeyID) var defTree *parse.Tree
if nil == destKey { var err error
return nil if (*treeCache)[defBlock.RootID] != nil {
defTree = (*treeCache)[defBlock.RootID]
} else {
defTree, err = LoadTreeByBlockID(defBlock.RootID)
if err != nil {
return ast.WalkSkipChildren
}
(*treeCache)[defBlock.RootID] = defTree
}
*defBlockIDs = append(*defBlockIDs, defID)
exportRefTrees(defTree, defBlockIDs, retTrees, treeCache)
} else if treenode.IsBlockLink(n) {
defID := strings.TrimPrefix(n.TextMarkAHref, "siyuan://blocks/")
if "" == defID {
return ast.WalkContinue
}
defBlock := treenode.GetBlockTree(defID)
if nil == defBlock {
return ast.WalkSkipChildren
} }
destView, _ := destAv.GetCurrentView(destAv.ViewID) var defTree *parse.Tree
if nil == destView { var err error
return nil if (*treeCache)[defBlock.RootID] != nil {
defTree = (*treeCache)[defBlock.RootID]
} else {
defTree, err = LoadTreeByBlockID(defBlock.RootID)
if err != nil {
return ast.WalkSkipChildren
}
(*treeCache)[defBlock.RootID] = defTree
}
*defBlockIDs = append(*defBlockIDs, defID)
exportRefTrees(defTree, defBlockIDs, retTrees, treeCache)
} else if ast.NodeAttributeView == n.Type {
// 导出数据库所在文档时一并导出绑定块所在文档
// Export the binding block docs when exporting the doc where the database is located https://github.com/siyuan-note/siyuan/issues/11486
avID := n.AttributeViewID
if "" == avID {
return ast.WalkContinue
} }
destTable := sql.RenderAttributeViewTable(destAv, destView, "") attrView, _ := av.ParseAttributeView(avID)
if nil == destTable { if nil == attrView {
return nil return ast.WalkContinue
} }
return destTable.GetColumn(rollupKey.Rollup.KeyID) blockKeyValues := attrView.GetBlockKeyValues()
if nil == blockKeyValues {
return ast.WalkContinue
}
for _, val := range blockKeyValues.Values {
defBlock := treenode.GetBlockTree(val.BlockID)
if nil == defBlock {
continue
}
var defTree *parse.Tree
var err error
if (*treeCache)[defBlock.RootID] != nil {
defTree = (*treeCache)[defBlock.RootID]
} else {
defTree, err = LoadTreeByBlockID(defBlock.RootID)
if err != nil {
continue
}
(*treeCache)[defBlock.RootID] = defTree
}
*defBlockIDs = append(*defBlockIDs, val.BlockID)
exportRefTrees(defTree, defBlockIDs, retTrees, treeCache)
}
}
return ast.WalkContinue
})
*defBlockIDs = gulu.Str.RemoveDuplicatedElem(*defBlockIDs)
}
func loadTreeWithCache(id string, treeCache *map[string]*parse.Tree) (tree *parse.Tree, err error) {
if tree = (*treeCache)[id]; nil != tree {
return
}
tree, err = LoadTreeByBlockID(id)
if nil == err && nil != tree {
(*treeCache)[id] = tree
}
return
} }