From 7fe1b494a8da824276bb39b1daec18b666926d53 Mon Sep 17 00:00:00 2001
From: Daniel <845765@qq.com>
Date: Fri, 30 Jan 2026 18:46:29 +0800
Subject: [PATCH 1/3] :art: https://github.com/siyuan-note/siyuan/issues/16941
Signed-off-by: Daniel <845765@qq.com>
---
app/src/mobile/settings/about.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/mobile/settings/about.ts b/app/src/mobile/settings/about.ts
index 498a4018e..b88e96637 100644
--- a/app/src/mobile/settings/about.ts
+++ b/app/src/mobile/settings/about.ts
@@ -63,7 +63,7 @@ export const initAbout = () => {
${window.siyuan.languages.about2}
-
+
${window.siyuan.languages.about4}
${window.siyuan.languages.about3.replace("${port}", location.port)}
From 46716ab502c50c40a27da1ac21a6fdcb406f6f5d Mon Sep 17 00:00:00 2001
From: Daniel <845765@qq.com>
Date: Fri, 30 Jan 2026 19:16:20 +0800
Subject: [PATCH 2/3] :art: Default create a select field for grouping in
Kanban view https://github.com/siyuan-note/siyuan/issues/16940
Signed-off-by: Daniel <845765@qq.com>
---
kernel/model/attribute_view.go | 22 +++++++++++++++++++++-
1 file changed, 21 insertions(+), 1 deletion(-)
diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go
index b1751a2d1..7a7e7ffca 100644
--- a/kernel/model/attribute_view.go
+++ b/kernel/model/attribute_view.go
@@ -3111,8 +3111,28 @@ func getKanbanPreferredGroupKey(attrView *av.AttributeView) (ret *av.Key) {
break
}
}
+
if nil == ret {
- ret = attrView.GetBlockKey()
+ name := av.GetAttributeViewI18n("select")
+ ret = av.NewKey(ast.NewNodeID(), name, "", av.KeyTypeSelect)
+ attrView.KeyValues = append(attrView.KeyValues, &av.KeyValues{Key: ret})
+ for _, view := range attrView.Views {
+ newField := &av.BaseField{ID: ret.ID}
+ if nil != view.Table {
+ newField.Wrap = view.Table.WrapField
+ view.Table.Columns = append(view.Table.Columns, &av.ViewTableColumn{BaseField: newField})
+ }
+
+ if nil != view.Gallery {
+ newField.Wrap = view.Gallery.WrapField
+ view.Gallery.CardFields = append(view.Gallery.CardFields, &av.ViewGalleryCardField{BaseField: newField})
+ }
+
+ if nil != view.Kanban {
+ newField.Wrap = view.Kanban.WrapField
+ view.Kanban.Fields = append(view.Kanban.Fields, &av.ViewKanbanField{BaseField: newField})
+ }
+ }
}
return
}
From acf168bb71f849bfad2f7eb3ce44520a3217987d Mon Sep 17 00:00:00 2001
From: Daniel <845765@qq.com>
Date: Fri, 30 Jan 2026 21:24:52 +0800
Subject: [PATCH 3/3] :bug: Fix
https://github.com/siyuan-note/siyuan/issues/16951
Signed-off-by: Daniel <845765@qq.com>
---
kernel/filesys/tree.go | 42 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 41 insertions(+), 1 deletion(-)
diff --git a/kernel/filesys/tree.go b/kernel/filesys/tree.go
index fcbefbc74..bbd4c293a 100644
--- a/kernel/filesys/tree.go
+++ b/kernel/filesys/tree.go
@@ -252,7 +252,7 @@ func prepareWriteTree(tree *parse.Tree) (data []byte, filePath string, err error
tree.Root.SetIALAttr("type", "doc")
renderer := render.NewJSONRenderer(tree, luteEngine.RenderOptions, luteEngine.ParseOptions)
data = renderer.Render()
- data = bytes.ReplaceAll(data, []byte(`\u0000`), []byte(""))
+ data = removeUnescapedUnicodeNull(data)
if !util.UseSingleLineSave {
buf := bytes.Buffer{}
buf.Grow(1024 * 1024 * 2)
@@ -269,6 +269,46 @@ func prepareWriteTree(tree *parse.Tree) (data []byte, filePath string, err error
return
}
+// removeUnescapedUnicodeNull 只移除未被转义的 `\u0000` 字面序列。
+// 判断方法:在匹配到 `\u0000` 时向前数连续的 `\` 个数,若为偶数则视为未转义并移除。
+func removeUnescapedUnicodeNull(data []byte) []byte {
+ patLen := 6 // len(`\u0000`)
+ n := len(data)
+ if n < patLen {
+ return data
+ }
+
+ dst := make([]byte, 0, n)
+ i := 0
+ for i < n {
+ // 快速检查是否可能匹配 `\u0000`
+ if i+patLen <= n &&
+ data[i] == '\\' &&
+ data[i+1] == 'u' &&
+ data[i+2] == '0' &&
+ data[i+3] == '0' &&
+ data[i+4] == '0' &&
+ data[i+5] == '0' {
+ // 统计当前 `\` 之前连续的反斜杠数量
+ j := i - 1
+ backslashes := 0
+ for j >= 0 && data[j] == '\\' {
+ backslashes++
+ j--
+ }
+ // 若为偶数,则当前 `\` 未被转义,跳过整个 `\u0000`
+ if backslashes%2 == 0 {
+ i += patLen
+ continue
+ }
+ }
+ // 否则保留当前字节
+ dst = append(dst, data[i])
+ i++
+ }
+ return dst
+}
+
func afterWriteTree(tree *parse.Tree) {
docIAL := parse.IAL2MapUnEsc(tree.Root.KramdownIAL)
cache.PutDocIAL(tree.Path, docIAL)