From cfcf695fb410765cbf9206dd831411f44bff7b09 Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Tue, 20 Jan 2026 09:41:29 +0800 Subject: [PATCH 1/8] :art: Improve export to Word .docx format https://github.com/siyuan-note/siyuan/issues/14970 Signed-off-by: Daniel <845765@qq.com> --- kernel/model/conf.go | 4 +++- kernel/util/pandoc.go | 38 ++++++++++++++++---------------------- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/kernel/model/conf.go b/kernel/model/conf.go index edb5cafa1..0493e333e 100644 --- a/kernel/model/conf.go +++ b/kernel/model/conf.go @@ -1272,7 +1272,9 @@ func subscribeConfEvents() { params += " \"" + util.PandocTemplatePath + "\"" Conf.Export.PandocParams = strings.TrimSpace(params) } - + + logging.LogInfof("pandoc params set to [%s]", Conf.Export.PandocParams) + logging.LogInfof("pandoc template [%s], color filter [%s]", util.PandocTemplatePath, util.PandocColorFilterPath) Conf.Save() }) } diff --git a/kernel/util/pandoc.go b/kernel/util/pandoc.go index c6831bf4a..1cf80595b 100644 --- a/kernel/util/pandoc.go +++ b/kernel/util/pandoc.go @@ -111,7 +111,7 @@ func InitPandoc() { return } - pandocDir := filepath.Join(TempDir, "pandoc") + tempPandocDir := filepath.Join(TempDir, "pandoc") if confPath := filepath.Join(ConfDir, "conf.json"); gulu.File.IsExist(confPath) { // Workspace built-in Pandoc is no longer initialized after customizing Pandoc path https://github.com/siyuan-note/siyuan/issues/8377 @@ -119,7 +119,7 @@ func InitPandoc() { conf := map[string]interface{}{} if err = gulu.JSON.UnmarshalJSON(data, &conf); err == nil && nil != conf["export"] { export := conf["export"].(map[string]interface{}) - if customPandocBinPath := export["pandocBin"].(string); !strings.HasPrefix(customPandocBinPath, pandocDir) { + if customPandocBinPath := export["pandocBin"].(string); !strings.HasPrefix(customPandocBinPath, tempPandocDir) { if pandocVer := getPandocVer(customPandocBinPath); "" != pandocVer { PandocBinPath = customPandocBinPath logging.LogInfof("custom pandoc [ver=%s, bin=%s]", pandocVer, PandocBinPath) @@ -130,24 +130,18 @@ func InitPandoc() { } } - PandocTemplatePath = filepath.Join(pandocDir, "pandoc-resources", "pandoc-template.docx") + PandocTemplatePath = filepath.Join(WorkingDir, "pandoc-resources", "pandoc-template.docx") if !gulu.File.IsExist(PandocTemplatePath) { - PandocTemplatePath = filepath.Join(WorkingDir, "pandoc-resources", "pandoc-template.docx") - if "dev" == Mode || !gulu.File.IsExist(PandocTemplatePath) { - PandocTemplatePath = filepath.Join(WorkingDir, "pandoc", "pandoc-resources", "pandoc-template.docx") - } + PandocTemplatePath = filepath.Join(WorkingDir, "pandoc", "pandoc-resources", "pandoc-template.docx") } if !gulu.File.IsExist(PandocTemplatePath) { PandocTemplatePath = "" logging.LogWarnf("pandoc template file [%s] not found", PandocTemplatePath) } - PandocColorFilterPath = filepath.Join(pandocDir, "pandoc-resources", "pandoc_color_filter.lua") + PandocColorFilterPath = filepath.Join(WorkingDir, "pandoc-resources", "pandoc_color_filter.lua") if !gulu.File.IsExist(PandocColorFilterPath) { - PandocColorFilterPath = filepath.Join(WorkingDir, "pandoc-resources", "pandoc_color_filter.lua") - if "dev" == Mode || !gulu.File.IsExist(PandocColorFilterPath) { - PandocColorFilterPath = filepath.Join(WorkingDir, "pandoc", "pandoc-resources", "pandoc_color_filter.lua") - } + PandocColorFilterPath = filepath.Join(WorkingDir, "pandoc", "pandoc-resources", "pandoc_color_filter.lua") } if !gulu.File.IsExist(PandocColorFilterPath) { PandocColorFilterPath = "" @@ -158,13 +152,13 @@ func InitPandoc() { if gulu.OS.IsWindows() { if "amd64" == runtime.GOARCH { - PandocBinPath = filepath.Join(pandocDir, "bin", "pandoc.exe") + PandocBinPath = filepath.Join(tempPandocDir, "bin", "pandoc.exe") } } else if gulu.OS.IsDarwin() { - PandocBinPath = filepath.Join(pandocDir, "bin", "pandoc") + PandocBinPath = filepath.Join(tempPandocDir, "bin", "pandoc") } else if gulu.OS.IsLinux() { if "amd64" == runtime.GOARCH { - PandocBinPath = filepath.Join(pandocDir, "bin", "pandoc") + PandocBinPath = filepath.Join(tempPandocDir, "bin", "pandoc") } } pandocVer := getPandocVer(PandocBinPath) @@ -174,22 +168,22 @@ func InitPandoc() { } pandocZip := filepath.Join(WorkingDir, "pandoc.zip") - if "dev" == Mode || !gulu.File.IsExist(pandocZip) { + if !gulu.File.IsExist(pandocZip) { if gulu.OS.IsWindows() { if "amd64" == runtime.GOARCH { - pandocZip = filepath.Join(WorkingDir, "pandoc/pandoc-windows-amd64.zip") + pandocZip = filepath.Join(WorkingDir, "pandoc", "pandoc-windows-amd64.zip") } } else if gulu.OS.IsDarwin() { if "amd64" == runtime.GOARCH { - pandocZip = filepath.Join(WorkingDir, "pandoc/pandoc-darwin-amd64.zip") + pandocZip = filepath.Join(WorkingDir, "pandoc", "pandoc-darwin-amd64.zip") } else if "arm64" == runtime.GOARCH { - pandocZip = filepath.Join(WorkingDir, "pandoc/pandoc-darwin-arm64.zip") + pandocZip = filepath.Join(WorkingDir, "pandoc", "pandoc-darwin-arm64.zip") } } else if gulu.OS.IsLinux() { if "amd64" == runtime.GOARCH { - pandocZip = filepath.Join(WorkingDir, "pandoc/pandoc-linux-amd64.zip") + pandocZip = filepath.Join(WorkingDir, "pandoc", "pandoc-linux-amd64.zip") } else if "arm64" == runtime.GOARCH { - pandocZip = filepath.Join(WorkingDir, "pandoc/pandoc-linux-arm64.zip") + pandocZip = filepath.Join(WorkingDir, "pandoc", "pandoc-linux-arm64.zip") } } } @@ -200,7 +194,7 @@ func InitPandoc() { return } - if err := gulu.Zip.Unzip(pandocZip, pandocDir); err != nil { + if err := gulu.Zip.Unzip(pandocZip, tempPandocDir); err != nil { logging.LogErrorf("unzip pandoc failed: %s", err) return } From f6e9ed5b553758cd10142b1fadec24794dade477 Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Tue, 20 Jan 2026 10:03:38 +0800 Subject: [PATCH 2/8] :art: Clean code Signed-off-by: Daniel <845765@qq.com> --- kernel/model/virutalref.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/kernel/model/virutalref.go b/kernel/model/virutalref.go index a3923806e..05405a6d3 100644 --- a/kernel/model/virutalref.go +++ b/kernel/model/virutalref.go @@ -45,9 +45,6 @@ var virtualBlockRefCache, _ = ristretto.NewCache(&ristretto.Config{ BufferItems: 64, }) -// newlineRegexp 用于匹配连续或单个换行符的正则表达式 -var newlineRegexp = regexp.MustCompile(`[\r\n]+`) - func getBlockVirtualRefKeywords(root *ast.Node) (ret []string) { val, ok := virtualBlockRefCache.Get(root.ID) if !ok { @@ -256,7 +253,7 @@ func parseKeywords(keywordsStr string) (keywords []string) { // 先处理转义的逗号 keywordsStr = strings.ReplaceAll(keywordsStr, "\\,", "__comma@sep__") // 再将连续或单个换行符替换为一个逗号,避免把 `\\\n` 转换为 `\,` - keywordsStr = newlineRegexp.ReplaceAllString(keywordsStr, ",") + keywordsStr = regexp.MustCompile(`[\r\n]+`).ReplaceAllString(keywordsStr, ",") // 按逗号分隔 for part := range strings.SplitSeq(keywordsStr, ",") { part = strings.TrimSpace(part) // 剔除前后的空白字符 From d5f7bf2c02fcf48817d9a2fc9090ee203441f297 Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Tue, 20 Jan 2026 10:05:28 +0800 Subject: [PATCH 3/8] :art: Clean code Signed-off-by: Daniel <845765@qq.com> --- kernel/model/virutalref.go | 2 +- kernel/util/rune.go | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/kernel/model/virutalref.go b/kernel/model/virutalref.go index 05405a6d3..5bb65de18 100644 --- a/kernel/model/virutalref.go +++ b/kernel/model/virutalref.go @@ -253,7 +253,7 @@ func parseKeywords(keywordsStr string) (keywords []string) { // 先处理转义的逗号 keywordsStr = strings.ReplaceAll(keywordsStr, "\\,", "__comma@sep__") // 再将连续或单个换行符替换为一个逗号,避免把 `\\\n` 转换为 `\,` - keywordsStr = regexp.MustCompile(`[\r\n]+`).ReplaceAllString(keywordsStr, ",") + keywordsStr = util.ReplaceNewline(keywordsStr, ",") // 按逗号分隔 for part := range strings.SplitSeq(keywordsStr, ",") { part = strings.TrimSpace(part) // 剔除前后的空白字符 diff --git a/kernel/util/rune.go b/kernel/util/rune.go index 9e58df926..6533508f0 100644 --- a/kernel/util/rune.go +++ b/kernel/util/rune.go @@ -27,6 +27,10 @@ import ( "github.com/siyuan-note/logging" ) +func ReplaceNewline(text, replaceWith string) string { + return regexp.MustCompile(`[\r\n]+`).ReplaceAllString(text, replaceWith) +} + func ContainsCJK(text string) bool { for _, r := range text { ret := unicode.Is(unicode.Han, r) || unicode.Is(unicode.Lm, r) || From e9564ff0ec49ef487c28cfe0d2ef331bca4e9113 Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Tue, 20 Jan 2026 10:05:49 +0800 Subject: [PATCH 4/8] :art: Supports setting Pandoc parameters for export docx https://github.com/siyuan-note/siyuan/issues/16845 Signed-off-by: Daniel <845765@qq.com> --- app/src/config/exportConfig.ts | 11 +++++------ kernel/api/setting.go | 1 + 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/config/exportConfig.ts b/app/src/config/exportConfig.ts index 324c4aa42..f76ce993f 100644 --- a/app/src/config/exportConfig.ts +++ b/app/src/config/exportConfig.ts @@ -165,12 +165,11 @@ export const exportConfig = { -
-
- ${window.siyuan.languages.export25} -
${window.siyuan.languages.export26}
-
- +
+ ${window.siyuan.languages.export25} +
${window.siyuan.languages.export26}
+
+
diff --git a/kernel/api/setting.go b/kernel/api/setting.go index 029b6e4b7..b03a17efb 100644 --- a/kernel/api/setting.go +++ b/kernel/api/setting.go @@ -379,6 +379,7 @@ func setExport(c *gin.Context) { } } + export.PandocParams = util.ReplaceNewline(export.PandocParams, " ") model.Conf.Export = export model.Conf.Save() From ffd947233e46c78beaa5cd38603838e5a9a93414 Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Tue, 20 Jan 2026 10:39:23 +0800 Subject: [PATCH 5/8] :art: Clean code Signed-off-by: Daniel <845765@qq.com> --- kernel/util/rune.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kernel/util/rune.go b/kernel/util/rune.go index 6533508f0..1617a3b39 100644 --- a/kernel/util/rune.go +++ b/kernel/util/rune.go @@ -27,8 +27,10 @@ import ( "github.com/siyuan-note/logging" ) +var newlinesRegex = regexp.MustCompile(`[\r\n]+`) + func ReplaceNewline(text, replaceWith string) string { - return regexp.MustCompile(`[\r\n]+`).ReplaceAllString(text, replaceWith) + return newlinesRegex.ReplaceAllString(text, replaceWith) } func ContainsCJK(text string) bool { From ef9d8116163c1056846513ec478c664613f01bed Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Tue, 20 Jan 2026 10:39:42 +0800 Subject: [PATCH 6/8] :art: https://github.com/siyuan-note/siyuan/issues/16854 Signed-off-by: Daniel <845765@qq.com> --- kernel/model/push_reload.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/kernel/model/push_reload.go b/kernel/model/push_reload.go index d0ba464ca..0bc6c8ac5 100644 --- a/kernel/model/push_reload.go +++ b/kernel/model/push_reload.go @@ -138,7 +138,10 @@ func refreshDocInfoWithSize(tree *parse.Tree, size uint64) { } refreshDocInfo0(tree, size) - refreshParentDocInfo(tree) + go func() { + time.Sleep(512 * time.Millisecond) + refreshParentDocInfo(tree) + }() } func refreshParentDocInfo(tree *parse.Tree) { From a64ab7664027af4a4ca89be6391efc34b66bcc53 Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Tue, 20 Jan 2026 10:45:58 +0800 Subject: [PATCH 7/8] :art: https://github.com/siyuan-note/siyuan/issues/16854 Signed-off-by: Daniel <845765@qq.com> --- kernel/model/push_reload.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/model/push_reload.go b/kernel/model/push_reload.go index 0bc6c8ac5..168d5291c 100644 --- a/kernel/model/push_reload.go +++ b/kernel/model/push_reload.go @@ -139,7 +139,7 @@ func refreshDocInfoWithSize(tree *parse.Tree, size uint64) { refreshDocInfo0(tree, size) go func() { - time.Sleep(512 * time.Millisecond) + time.Sleep(128 * time.Millisecond) refreshParentDocInfo(tree) }() } From 48f26f9247aa5ca81f72523b523313461bea511e Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Tue, 20 Jan 2026 11:16:14 +0800 Subject: [PATCH 8/8] :art: The image address bar is automatically converted to an asset after filling in Base64 https://github.com/siyuan-note/siyuan/issues/16132 Signed-off-by: Daniel <845765@qq.com> --- kernel/model/import.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kernel/model/import.go b/kernel/model/import.go index 45053ba55..cf64c9cb1 100644 --- a/kernel/model/import.go +++ b/kernel/model/import.go @@ -1282,6 +1282,11 @@ func processBase64Img(n *ast.Node, dest string, assetDirPath string) { case "image/png": img, decodeErr = png.Decode(dataReader) ext = ".png" + if nil != decodeErr { + dataReader.Seek(0, 0) + img, decodeErr = jpeg.Decode(dataReader) + ext = ".jpg" + } case "image/jpeg": img, decodeErr = jpeg.Decode(dataReader) ext = ".jpg"