Merge branch 'dev' into master

This commit is contained in:
Ömer Gürbüz 2025-12-13 23:38:12 +03:00 committed by GitHub
commit c3cd6e3b8a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
409 changed files with 5557 additions and 1327 deletions

View file

@ -28,6 +28,7 @@ import (
"github.com/siyuan-note/logging"
"github.com/siyuan-note/siyuan/kernel/filesys"
"github.com/siyuan-note/siyuan/kernel/model"
"github.com/siyuan-note/siyuan/kernel/treenode"
"github.com/siyuan-note/siyuan/kernel/util"
)
@ -660,6 +661,10 @@ func getBlockInfo(c *gin.Context) {
ret.Code = 3
ret.Msg = model.Conf.Language(56)
return
} else if errors.Is(err, treenode.ErrSpecTooNew) {
ret.Code = -1
ret.Msg = model.Conf.Language(275)
return
}
block, _ := model.GetBlock(id, tree)

View file

@ -903,7 +903,6 @@ func batchUpdateBlock(c *gin.Context) {
ret.Data = transactions
broadcastTransactions(transactions)
}
func deleteBlock(c *gin.Context) {

View file

@ -733,7 +733,7 @@ func exportPreview(c *gin.Context) {
}
}
stdHTML := model.Preview(id, fillCSSVar)
stdHTML := model.ExportPreview(id, fillCSSVar)
ret.Data = map[string]interface{}{
"html": stdHTML,
"fillCSSVar": fillCSSVar,

View file

@ -380,10 +380,13 @@ func putFile(c *gin.Context) {
return
}
if !util.IsValidUploadFileName(filepath.Base(fileAbsPath)) { // Improve kernel API `/api/file/putFile` parameter validation https://github.com/siyuan-note/siyuan/issues/14658
ret.Code = http.StatusBadRequest
ret.Msg = "invalid file path, please check https://github.com/siyuan-note/siyuan/issues/14658 for more details"
return
fileExists := filelock.IsExist(fileAbsPath)
if !fileExists {
if !util.IsValidUploadFileName(filepath.Base(fileAbsPath)) { // Improve kernel API `/api/file/putFile` parameter validation https://github.com/siyuan-note/siyuan/issues/14658
ret.Code = http.StatusBadRequest
ret.Msg = "invalid file path, please check https://github.com/siyuan-note/siyuan/issues/14658 for more details"
return
}
}
isDirStr := c.PostForm("isDir")

View file

@ -68,9 +68,9 @@ func setPetalEnabled(c *gin.Context) {
}
if enabled {
upsertPluginCodeSet := hashset.New(packageName)
model.PushReloadPlugin(upsertPluginCodeSet, nil, nil, app)
model.PushReloadPlugin(upsertPluginCodeSet, nil, nil, nil, app)
} else {
removePluginSet := hashset.New(packageName)
model.PushReloadPlugin(nil, nil, removePluginSet, app)
unloadPluginSet := hashset.New(packageName)
model.PushReloadPlugin(nil, nil, unloadPluginSet, nil, app)
}
}

View file

@ -326,6 +326,7 @@ func setEditor(c *gin.Context) {
model.Conf.Save()
if oldGenerateHistoryInterval != model.Conf.Editor.GenerateHistoryInterval {
model.GenerateFileHistory()
model.ChangeHistoryTick(editor.GenerateHistoryInterval)
}

View file

@ -785,7 +785,13 @@ func exit(c *gin.Context) {
execInstallPkg = int(execInstallPkgArg.(float64))
}
exitCode := model.Close(force, true, execInstallPkg)
setCurrentWorkspaceArg := arg["setCurrentWorkspace"]
setCurrentWorkspace := true
if nil != setCurrentWorkspaceArg {
setCurrentWorkspace = setCurrentWorkspaceArg.(bool)
}
exitCode := model.Close(force, setCurrentWorkspace, execInstallPkg)
ret.Code = exitCode
switch exitCode {
case 0:

View file

@ -330,9 +330,7 @@ func setWorkspaceDir(c *gin.Context) {
if util.ContainerAndroid == util.Container || util.ContainerIOS == util.Container || util.ContainerHarmony == util.Container {
util.PushMsg(model.Conf.Language(42), 1000*15)
time.Sleep(time.Second * 1)
model.Close(false, false, 1)
time.Sleep(time.Second * 1)
time.Sleep(2 * time.Second)
}
}

View file

@ -169,9 +169,9 @@ func InstalledIcons() (ret []*Icon) {
icon.PreferredFunding = getPreferredFunding(icon.Funding)
icon.PreferredName = GetPreferredName(icon.Package)
icon.PreferredDesc = getPreferredDesc(icon.Description)
info, statErr := os.Stat(filepath.Join(installPath, "README.md"))
info, statErr := os.Stat(filepath.Join(installPath, "icon.json"))
if nil != statErr {
logging.LogWarnf("stat install theme README.md failed: %s", statErr)
logging.LogWarnf("stat install icon.json failed: %s", statErr)
continue
}
icon.HInstallDate = info.ModTime().Format("2006-01-02")
@ -183,14 +183,7 @@ func InstalledIcons() (ret []*Icon) {
packageInstallSizeCache.SetDefault(icon.RepoURL, is)
}
icon.HInstallSize = humanize.BytesCustomCeil(uint64(icon.InstallSize), 2)
readmeFilename := getPreferredReadme(icon.Readme)
readme, readErr := os.ReadFile(filepath.Join(installPath, readmeFilename))
if nil != readErr {
logging.LogWarnf("read installed README.md failed: %s", readErr)
continue
}
icon.PreferredReadme, _ = renderLocalREADME("/appearance/icons/"+dirName+"/", readme)
icon.PreferredReadme = loadInstalledReadme(installPath, "/appearance/icons/"+dirName+"/", icon.Readme)
icon.Outdated = isOutdatedIcon(icon, bazaarIcons)
ret = append(ret, icon)
}

View file

@ -50,6 +50,7 @@ type DisplayName struct {
HeIL string `json:"he_IL"`
ItIT string `json:"it_IT"`
JaJP string `json:"ja_JP"`
KoKR string `json:"ko_KR"`
PlPL string `json:"pl_PL"`
PtBR string `json:"pt_BR"`
RuRU string `json:"ru_RU"`
@ -68,6 +69,7 @@ type Description struct {
HeIL string `json:"he_IL"`
ItIT string `json:"it_IT"`
JaJP string `json:"ja_JP"`
KoKR string `json:"ko_KR"`
PlPL string `json:"pl_PL"`
PtBR string `json:"pt_BR"`
RuRU string `json:"ru_RU"`
@ -86,6 +88,7 @@ type Readme struct {
HeIL string `json:"he_IL"`
ItIT string `json:"it_IT"`
JaJP string `json:"ja_JP"`
KoKR string `json:"ko_KR"`
PlPL string `json:"pl_PL"`
PtBR string `json:"pt_BR"`
RuRU string `json:"ru_RU"`
@ -174,7 +177,7 @@ func getPreferredReadme(readme *Readme) string {
return "README.md"
}
ret := readme.Default
var ret string
switch util.Lang {
case "ar_SA":
if "" != readme.ArSA {
@ -208,6 +211,10 @@ func getPreferredReadme(readme *Readme) string {
if "" != readme.JaJP {
ret = readme.JaJP
}
case "ko_KR":
if "" != readme.KoKR {
ret = readme.KoKR
}
case "pl_PL":
if "" != readme.PlPL {
ret = readme.PlPL
@ -232,13 +239,14 @@ func getPreferredReadme(readme *Readme) string {
if "" != readme.ZhCN {
ret = readme.ZhCN
}
default:
if "" != readme.EnUS {
ret = readme.EnUS
}
}
if "" == ret {
ret = "README.md"
if "" == strings.TrimSpace(ret) {
defaultReadme := strings.TrimSpace(readme.Default)
if defaultReadme != "" {
ret = defaultReadme
} else {
ret = "README.md"
}
}
return ret
}
@ -248,7 +256,7 @@ func GetPreferredName(pkg *Package) string {
return pkg.Name
}
ret := pkg.DisplayName.Default
var ret string
switch util.Lang {
case "ar_SA":
if "" != pkg.DisplayName.ArSA {
@ -282,6 +290,10 @@ func GetPreferredName(pkg *Package) string {
if "" != pkg.DisplayName.JaJP {
ret = pkg.DisplayName.JaJP
}
case "ko_KR":
if "" != pkg.DisplayName.KoKR {
ret = pkg.DisplayName.KoKR
}
case "pl_PL":
if "" != pkg.DisplayName.PlPL {
ret = pkg.DisplayName.PlPL
@ -306,13 +318,14 @@ func GetPreferredName(pkg *Package) string {
if "" != pkg.DisplayName.ZhCN {
ret = pkg.DisplayName.ZhCN
}
default:
if "" != pkg.DisplayName.EnUS {
ret = pkg.DisplayName.EnUS
}
}
if "" == ret {
ret = pkg.Name
if "" == strings.TrimSpace(ret) {
defaultName := strings.TrimSpace(pkg.DisplayName.Default)
if defaultName != "" {
ret = defaultName
} else {
ret = pkg.Name
}
}
return ret
}
@ -322,7 +335,7 @@ func getPreferredDesc(desc *Description) string {
return ""
}
ret := desc.Default
var ret string
switch util.Lang {
case "ar_SA":
if "" != desc.ArSA {
@ -356,6 +369,10 @@ func getPreferredDesc(desc *Description) string {
if "" != desc.JaJP {
ret = desc.JaJP
}
case "ko_KR":
if "" != desc.KoKR {
ret = desc.KoKR
}
case "pl_PL":
if "" != desc.PlPL {
ret = desc.PlPL
@ -380,13 +397,12 @@ func getPreferredDesc(desc *Description) string {
if "" != desc.ZhCN {
ret = desc.ZhCN
}
default:
if "" != desc.EnUS {
ret = desc.EnUS
}
}
if "" == ret {
ret = desc.EnUS
if "" == strings.TrimSpace(ret) {
defaultDesc := strings.TrimSpace(desc.Default)
if defaultDesc != "" {
ret = defaultDesc
}
}
return ret
}
@ -397,12 +413,21 @@ func getPreferredFunding(funding *Funding) string {
}
if "" != funding.OpenCollective {
if strings.HasPrefix(funding.OpenCollective, "http://") || strings.HasPrefix(funding.OpenCollective, "https://") {
return funding.OpenCollective
}
return "https://opencollective.com/" + funding.OpenCollective
}
if "" != funding.Patreon {
if strings.HasPrefix(funding.Patreon, "http://") || strings.HasPrefix(funding.Patreon, "https://") {
return funding.Patreon
}
return "https://www.patreon.com/" + funding.Patreon
}
if "" != funding.GitHub {
if strings.HasPrefix(funding.GitHub, "http://") || strings.HasPrefix(funding.GitHub, "https://") {
return funding.GitHub
}
return "https://github.com/sponsors/" + funding.GitHub
}
if 0 < len(funding.Custom) {
@ -684,14 +709,29 @@ func GetPackageREADME(repoURL, repoHash, packageType string) (ret string) {
data, err := downloadPackage(repoURLHash+"/"+readme, false, "")
if err != nil {
ret = fmt.Sprintf("Load bazaar package's README.md(%s) failed: %s", readme, err.Error())
if readme == repo.Package.Readme.Default || "" == strings.TrimSpace(repo.Package.Readme.Default) {
return
ret = fmt.Sprintf("Load bazaar package's preferred README(%s) failed: %s", readme, err.Error())
// 回退到 Default README
var defaultReadme string
if nil != repo.Package.Readme {
defaultReadme = repo.Package.Readme.Default
}
readme = repo.Package.Readme.Default
data, err = downloadPackage(repoURLHash+"/"+readme, false, "")
if err != nil {
ret += fmt.Sprintf("<br>Load bazaar package's README.md(%s) failed: %s", readme, err.Error())
if "" == strings.TrimSpace(defaultReadme) {
defaultReadme = "README.md"
}
if readme != defaultReadme {
data, err = downloadPackage(repoURLHash+"/"+defaultReadme, false, "")
if err != nil {
ret += fmt.Sprintf("<br>Load bazaar package's default README(%s) failed: %s", defaultReadme, err.Error())
}
}
// 回退到 README.md
if err != nil && readme != "README.md" && defaultReadme != "README.md" {
data, err = downloadPackage(repoURLHash+"/README.md", false, "")
if err != nil {
ret += fmt.Sprintf("<br>Load bazaar package's README.md failed: %s", err.Error())
return
}
} else if err != nil {
return
}
}
@ -708,6 +748,46 @@ func GetPackageREADME(repoURL, repoHash, packageType string) (ret string) {
return
}
func loadInstalledReadme(installPath, basePath string, readme *Readme) (ret string) {
readmeFilename := getPreferredReadme(readme)
readmeData, readErr := os.ReadFile(filepath.Join(installPath, readmeFilename))
if nil == readErr {
ret, _ = renderLocalREADME(basePath, readmeData)
return
}
logging.LogWarnf("read installed %s failed: %s", readmeFilename, readErr)
ret = fmt.Sprintf("File %s not found", readmeFilename)
// 回退到 Default README
var defaultReadme string
if nil != readme {
defaultReadme = strings.TrimSpace(readme.Default)
}
if "" == defaultReadme {
defaultReadme = "README.md"
}
if readmeFilename != defaultReadme {
readmeData, readErr = os.ReadFile(filepath.Join(installPath, defaultReadme))
if nil == readErr {
ret, _ = renderLocalREADME(basePath, readmeData)
return
}
logging.LogWarnf("read installed %s failed: %s", defaultReadme, readErr)
ret += fmt.Sprintf("<br>File %s not found", defaultReadme)
}
// 回退到 README.md
if nil != readErr && readmeFilename != "README.md" && defaultReadme != "README.md" {
readmeData, readErr = os.ReadFile(filepath.Join(installPath, "README.md"))
if nil == readErr {
ret, _ = renderLocalREADME(basePath, readmeData)
return
}
logging.LogWarnf("read installed README.md failed: %s", readErr)
ret += "<br>File README.md not found"
}
return
}
func renderREADME(repoURL string, mdData []byte) (ret string, err error) {
luteEngine := lute.New()
luteEngine.SetSoftBreak2HardBreak(false)

View file

@ -207,9 +207,9 @@ func InstalledPlugins(frontend string, checkUpdate bool) (ret []*Plugin) {
plugin.PreferredFunding = getPreferredFunding(plugin.Funding)
plugin.PreferredName = GetPreferredName(plugin.Package)
plugin.PreferredDesc = getPreferredDesc(plugin.Description)
info, statErr := os.Stat(filepath.Join(installPath, "README.md"))
info, statErr := os.Stat(filepath.Join(installPath, "plugin.json"))
if nil != statErr {
logging.LogWarnf("stat install theme README.md failed: %s", statErr)
logging.LogWarnf("stat install plugin.json failed: %s", statErr)
continue
}
plugin.HInstallDate = info.ModTime().Format("2006-01-02")
@ -221,14 +221,7 @@ func InstalledPlugins(frontend string, checkUpdate bool) (ret []*Plugin) {
packageInstallSizeCache.SetDefault(plugin.RepoURL, is)
}
plugin.HInstallSize = humanize.BytesCustomCeil(uint64(plugin.InstallSize), 2)
readmeFilename := getPreferredReadme(plugin.Readme)
readme, readErr := os.ReadFile(filepath.Join(installPath, readmeFilename))
if nil != readErr {
logging.LogWarnf("read installed README.md failed: %s", readErr)
continue
}
plugin.PreferredReadme, _ = renderLocalREADME("/plugins/"+dirName+"/", readme)
plugin.PreferredReadme = loadInstalledReadme(installPath, "/plugins/"+dirName+"/", plugin.Readme)
plugin.Outdated = isOutdatedPlugin(plugin, bazaarPlugins)
plugin.Incompatible = isIncompatiblePlugin(plugin, frontend)
ret = append(ret, plugin)

View file

@ -170,9 +170,9 @@ func InstalledTemplates() (ret []*Template) {
template.PreferredFunding = getPreferredFunding(template.Funding)
template.PreferredName = GetPreferredName(template.Package)
template.PreferredDesc = getPreferredDesc(template.Description)
info, statErr := os.Stat(filepath.Join(installPath, "README.md"))
info, statErr := os.Stat(filepath.Join(installPath, "template.json"))
if nil != statErr {
logging.LogWarnf("stat install theme README.md failed: %s", statErr)
logging.LogWarnf("stat install template.json failed: %s", statErr)
continue
}
template.HInstallDate = info.ModTime().Format("2006-01-02")
@ -184,14 +184,7 @@ func InstalledTemplates() (ret []*Template) {
packageInstallSizeCache.SetDefault(template.RepoURL, is)
}
template.HInstallSize = humanize.BytesCustomCeil(uint64(template.InstallSize), 2)
readmeFilename := getPreferredReadme(template.Readme)
readme, readErr := os.ReadFile(filepath.Join(installPath, readmeFilename))
if nil != readErr {
logging.LogWarnf("read installed README.md failed: %s", readErr)
continue
}
template.PreferredReadme, _ = renderLocalREADME("/templates/"+dirName+"/", readme)
template.PreferredReadme = loadInstalledReadme(installPath, "/templates/"+dirName+"/", template.Readme)
template.Outdated = isOutdatedTemplate(template, bazaarTemplates)
ret = append(ret, template)
}

View file

@ -171,9 +171,9 @@ func InstalledThemes() (ret []*Theme) {
theme.PreferredFunding = getPreferredFunding(theme.Funding)
theme.PreferredName = GetPreferredName(theme.Package)
theme.PreferredDesc = getPreferredDesc(theme.Description)
info, statErr := os.Stat(filepath.Join(installPath, "README.md"))
info, statErr := os.Stat(filepath.Join(installPath, "theme.json"))
if nil != statErr {
logging.LogWarnf("stat install theme README.md failed: %s", statErr)
logging.LogWarnf("stat install theme.json failed: %s", statErr)
continue
}
theme.HInstallDate = info.ModTime().Format("2006-01-02")
@ -185,14 +185,7 @@ func InstalledThemes() (ret []*Theme) {
packageInstallSizeCache.SetDefault(theme.RepoURL, is)
}
theme.HInstallSize = humanize.BytesCustomCeil(uint64(theme.InstallSize), 2)
readmeFilename := getPreferredReadme(theme.Readme)
readme, readErr := os.ReadFile(filepath.Join(installPath, readmeFilename))
if nil != readErr {
logging.LogWarnf("read installed README.md failed: %s", readErr)
continue
}
theme.PreferredReadme, _ = renderLocalREADME("/appearance/themes/"+dirName+"/", readme)
theme.PreferredReadme = loadInstalledReadme(installPath, "/appearance/themes/"+dirName+"/", theme.Readme)
theme.Outdated = isOutdatedTheme(theme, bazaarThemes)
ret = append(ret, theme)
}

View file

@ -167,9 +167,9 @@ func InstalledWidgets() (ret []*Widget) {
widget.PreferredFunding = getPreferredFunding(widget.Funding)
widget.PreferredName = GetPreferredName(widget.Package)
widget.PreferredDesc = getPreferredDesc(widget.Description)
info, statErr := os.Stat(filepath.Join(installPath, "README.md"))
info, statErr := os.Stat(filepath.Join(installPath, "widget.json"))
if nil != statErr {
logging.LogWarnf("stat install theme README.md failed: %s", statErr)
logging.LogWarnf("stat install widget.json failed: %s", statErr)
continue
}
widget.HInstallDate = info.ModTime().Format("2006-01-02")
@ -181,14 +181,7 @@ func InstalledWidgets() (ret []*Widget) {
packageInstallSizeCache.SetDefault(widget.RepoURL, is)
}
widget.HInstallSize = humanize.BytesCustomCeil(uint64(widget.InstallSize), 2)
readmeFilename := getPreferredReadme(widget.Readme)
readme, readErr := os.ReadFile(filepath.Join(installPath, readmeFilename))
if nil != readErr {
logging.LogWarnf("read installed README.md failed: %s", readErr)
continue
}
widget.PreferredReadme, _ = renderLocalREADME("/widgets/"+dirName+"/", readme)
widget.PreferredReadme = loadInstalledReadme(installPath, "/widgets/"+dirName+"/", widget.Readme)
widget.Outdated = isOutdatedWidget(widget, bazaarWidgets)
ret = append(ret, widget)
}

View file

@ -48,6 +48,7 @@ type Editor struct {
Justify bool `json:"justify"` // 是否两端对齐
RTL bool `json:"rtl"` // 是否从右到左显示
Spellcheck bool `json:"spellcheck"` // 是否启用拼写检查
SpellcheckLanguages []string `json:"spellcheckLanguages"` // 拼写检查语言
OnlySearchForDoc bool `json:"onlySearchForDoc"` // 是否启用 [[ 仅搜索文档块
BacklinkExpandCount int `json:"backlinkExpandCount"` // 反向链接默认展开数量
BackmentionExpandCount int `json:"backmentionExpandCount"` // 反链提及默认展开数量
@ -88,6 +89,8 @@ func NewEditor() *Editor {
DynamicLoadBlocks: 192,
Justify: false,
RTL: false,
Spellcheck: false,
SpellcheckLanguages: []string{"en-US"},
BacklinkExpandCount: 8,
BackmentionExpandCount: -1,
BacklinkContainChildren: true,

View file

@ -72,6 +72,7 @@ type TypeFilter struct {
ListItem bool `json:"listItem"`
Blockquote bool `json:"blockquote"`
Super bool `json:"super"`
Callout bool `json:"callout"`
}
type D3 struct {

View file

@ -42,6 +42,7 @@ type Search struct {
VideoBlock bool `json:"videoBlock"`
IFrameBlock bool `json:"iframeBlock"`
WidgetBlock bool `json:"widgetBlock"`
Callout bool `json:"callout"`
Limit int `json:"limit"`
CaseSensitive bool `json:"caseSensitive"`
@ -84,6 +85,7 @@ func NewSearch() *Search {
VideoBlock: false,
IFrameBlock: false,
WidgetBlock: false,
Callout: false,
Limit: 64,
CaseSensitive: false,
@ -227,6 +229,12 @@ func (s *Search) TypeFilter() string {
buf.WriteByte('\'')
buf.WriteString(",")
}
if s.Callout {
buf.WriteByte('\'')
buf.WriteString(treenode.TypeAbbr(ast.NodeCallout.String()))
buf.WriteByte('\'')
buf.WriteString(",")
}
ret := buf.String()
if "" == ret {

View file

@ -125,10 +125,9 @@ func LoadTree(boxID, p string, luteEngine *lute.Lute) (ret *parse.Tree, err erro
}
func LoadTreeByData(data []byte, boxID, p string, luteEngine *lute.Lute) (ret *parse.Tree, err error) {
ret = parseJSON2Tree(boxID, p, data, luteEngine)
if nil == ret {
logging.LogErrorf("parse tree [%s] failed", p)
err = errors.New("parse tree failed")
ret, err = parseJSON2Tree(boxID, p, data, luteEngine)
if nil != err {
logging.LogErrorf("parse tree [%s] failed: %s", p, err)
return
}
ret.Path = p
@ -245,12 +244,9 @@ func prepareWriteTree(tree *parse.Tree) (data []byte, filePath string, err error
treenode.UpsertBlockTree(tree)
}
treenode.UpgradeSpec(tree)
filePath = filepath.Join(util.DataDir, tree.Box, tree.Path)
if oldSpec := tree.Root.Spec; "" == oldSpec {
parse.NestedInlines2FlattedSpans(tree, false)
tree.Root.Spec = "1"
logging.LogInfof("migrated tree [%s] from spec [%s] to [%s]", filePath, oldSpec, tree.Root.Spec)
}
tree.Root.SetIALAttr("type", "doc")
renderer := render.NewJSONRenderer(tree, luteEngine.RenderOptions)
data = renderer.Render()
@ -277,8 +273,7 @@ func afterWriteTree(tree *parse.Tree) {
cache.PutDocIAL(tree.Path, docIAL)
}
func parseJSON2Tree(boxID, p string, jsonData []byte, luteEngine *lute.Lute) (ret *parse.Tree) {
var err error
func parseJSON2Tree(boxID, p string, jsonData []byte, luteEngine *lute.Lute) (ret *parse.Tree, err error) {
var needFix bool
ret, needFix, err = dataparser.ParseJSON(jsonData, luteEngine.ParseOptions)
if err != nil {
@ -289,12 +284,12 @@ func parseJSON2Tree(boxID, p string, jsonData []byte, luteEngine *lute.Lute) (re
ret.Box = boxID
ret.Path = p
filePath := filepath.Join(util.DataDir, ret.Box, ret.Path)
if oldSpec := ret.Root.Spec; "" == oldSpec {
parse.NestedInlines2FlattedSpans(ret, false)
ret.Root.Spec = "1"
if err = treenode.CheckSpec(ret); errors.Is(err, treenode.ErrSpecTooNew) {
return
}
if treenode.UpgradeSpec(ret) {
needFix = true
logging.LogInfof("migrated tree [%s] from spec [%s] to [%s]", filePath, oldSpec, ret.Root.Spec)
}
if pathID := util.GetTreeID(p); pathID != ret.Root.ID {
@ -318,6 +313,7 @@ func parseJSON2Tree(boxID, p string, jsonData []byte, luteEngine *lute.Lute) (re
data = buf.Bytes()
}
filePath := filepath.Join(util.DataDir, ret.Box, ret.Path)
if err = os.MkdirAll(filepath.Dir(filePath), 0755); err != nil {
return
}

View file

@ -7,8 +7,8 @@ require (
github.com/88250/clipboard v0.1.5
github.com/88250/epub v0.0.0-20230830085737-c19055cd1f48
github.com/88250/go-humanize v0.0.0-20240424102817-4f78fac47ea7
github.com/88250/gulu v1.2.3-0.20251119142510-7b1583ab4aa0
github.com/88250/lute v1.7.7-0.20251201033723-6a6624a61082
github.com/88250/gulu v1.2.3-0.20251208021445-f93f2666eaac
github.com/88250/lute v1.7.7-0.20251212092708-7bcfc87de402
github.com/88250/vitess-sqlparser v0.0.0-20210205111146-56a2ded2aba1
github.com/ClarkThan/ahocorasick v0.0.0-20231011042242-30d1ef1347f4
github.com/ConradIrwin/font v0.2.1
@ -29,8 +29,8 @@ require (
github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb
github.com/flopp/go-findfont v0.1.0
github.com/fsnotify/fsnotify v1.9.0
github.com/gabriel-vasile/mimetype v1.4.10
github.com/gin-contrib/gzip v1.2.3
github.com/gabriel-vasile/mimetype v1.4.11
github.com/gin-contrib/gzip v1.2.5
github.com/gin-contrib/sessions v1.0.4
github.com/gin-contrib/sse v1.1.0
github.com/gin-gonic/gin v1.11.0
@ -53,17 +53,17 @@ require (
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/pdfcpu/pdfcpu v0.11.0
github.com/radovskyb/watcher v1.0.7
github.com/rqlite/sql v0.0.0-20251114131613-ef07423e7137
github.com/rqlite/sql v0.0.0-20251204023435-65660522892e
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06
github.com/sashabaranov/go-openai v1.41.2
github.com/shirou/gopsutil/v4 v4.25.11
github.com/siyuan-note/dataparser v0.0.0-20250804100744-b41253b236f3
github.com/siyuan-note/dejavu v0.0.0-20251202041457-7402b7c625c5
github.com/siyuan-note/dataparser v0.0.0-20251203120213-59c16535cb56
github.com/siyuan-note/dejavu v0.0.0-20251206130752-28126fa5ecd1
github.com/siyuan-note/encryption v0.0.0-20251120032857-3ddc3c2cc49f
github.com/siyuan-note/eventbus v0.0.0-20240627125516-396fdb0f0f97
github.com/siyuan-note/filelock v0.0.0-20251107023958-207cad31f0dd
github.com/siyuan-note/filelock v0.0.0-20251212095217-08318833e008
github.com/siyuan-note/httpclient v0.0.0-20251119144307-63b815d7d198
github.com/siyuan-note/logging v0.0.0-20251107023700-cd4339891032
github.com/siyuan-note/logging v0.0.0-20251209020516-52f1a2f65ec5
github.com/siyuan-note/riff v0.0.0-20251022131846-228528e70754
github.com/spf13/cast v1.10.0
github.com/steambap/captcha v1.4.1
@ -72,12 +72,12 @@ require (
github.com/vmihailenco/msgpack/v5 v5.4.1
github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342
github.com/xuri/excelize/v2 v2.9.0
golang.org/x/image v0.32.0
golang.org/x/mobile v0.0.0-20251009145931-8baca8bf4eeb
golang.org/x/mod v0.29.0
golang.org/x/net v0.47.0
golang.org/x/sys v0.38.0
golang.org/x/text v0.31.0
golang.org/x/image v0.33.0
golang.org/x/mobile v0.0.0-20251126181937-5c265dc024c4
golang.org/x/mod v0.30.0
golang.org/x/net v0.48.0
golang.org/x/sys v0.39.0
golang.org/x/text v0.32.0
golang.org/x/time v0.14.0
gopkg.in/yaml.v3 v3.0.1
)
@ -94,24 +94,24 @@ require (
github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82 // indirect
github.com/andybalholm/brotli v1.2.0 // indirect
github.com/andybalholm/cascadia v1.3.3 // indirect
github.com/aws/aws-sdk-go-v2 v1.40.0 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.3 // indirect
github.com/aws/aws-sdk-go-v2/config v1.32.2 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.19.2 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.14 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.14 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.14 // indirect
github.com/aws/aws-sdk-go-v2 v1.40.1 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4 // indirect
github.com/aws/aws-sdk-go-v2/config v1.32.3 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.19.3 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.15 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.15 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.15 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.14 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.5 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.14 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.14 // indirect
github.com/aws/aws-sdk-go-v2/service/s3 v1.92.1 // indirect
github.com/aws/aws-sdk-go-v2/service/signin v1.0.2 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.30.5 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.10 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.41.2 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.15 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.6 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.15 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.15 // indirect
github.com/aws/aws-sdk-go-v2/service/s3 v1.93.0 // indirect
github.com/aws/aws-sdk-go-v2/service/signin v1.0.3 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.30.6 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.11 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.41.3 // indirect
github.com/aws/smithy-go v1.24.0 // indirect
github.com/bytedance/gopkg v0.1.3 // indirect
github.com/bytedance/sonic v1.14.1 // indirect
@ -183,10 +183,10 @@ require (
github.com/xuri/efp v0.0.1 // indirect
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
golang.org/x/arch v0.22.0 // indirect
golang.org/x/crypto v0.45.0 // indirect
golang.org/x/sync v0.18.0 // indirect
golang.org/x/tools v0.38.0 // indirect
golang.org/x/arch v0.23.0 // indirect
golang.org/x/crypto v0.46.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/tools v0.39.0 // indirect
google.golang.org/protobuf v1.36.10 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
modernc.org/fileutil v1.3.40 // indirect

View file

@ -12,10 +12,10 @@ github.com/88250/go-humanize v0.0.0-20240424102817-4f78fac47ea7 h1:MafIFwSS0x6A4
github.com/88250/go-humanize v0.0.0-20240424102817-4f78fac47ea7/go.mod h1:HrKCCTin3YNDSLBD02K0AOljjV6eNwc3/zyEI+xyV1I=
github.com/88250/go-sqlite3 v1.14.13-0.20231214121541-e7f54c482950 h1:Pa5hMiBceTVVqrYaDlLio2QSKbXMUmAZPbzCwT5eNCw=
github.com/88250/go-sqlite3 v1.14.13-0.20231214121541-e7f54c482950/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/88250/gulu v1.2.3-0.20251119142510-7b1583ab4aa0 h1:ip0IQCJCLtJEDHil+2XSnh3NP39i98SYV5qhWoUeMnA=
github.com/88250/gulu v1.2.3-0.20251119142510-7b1583ab4aa0/go.mod h1:IQ5dXW9CjVmx6B7OfK1Y4ZBKTPMe9q1AkVoLGGzRbS8=
github.com/88250/lute v1.7.7-0.20251201033723-6a6624a61082 h1:iKn0t9LqKcVFl8CUlxiOIuf8t/4guLvaFFTHQ4IgZd4=
github.com/88250/lute v1.7.7-0.20251201033723-6a6624a61082/go.mod h1:WYyUw//5yVw9BJnoVjx7rI/3szsISxNZCYGOqTIrV0o=
github.com/88250/gulu v1.2.3-0.20251208021445-f93f2666eaac h1:EC80pY8zyR0gbL8ZLIBB4IPG/ia3ZHScrR/xt8zU8qU=
github.com/88250/gulu v1.2.3-0.20251208021445-f93f2666eaac/go.mod h1:IQ5dXW9CjVmx6B7OfK1Y4ZBKTPMe9q1AkVoLGGzRbS8=
github.com/88250/lute v1.7.7-0.20251212092708-7bcfc87de402 h1:Z+WVdMJVUAkwydjaavh22rLp/ODzdm+5jzYx037y3SA=
github.com/88250/lute v1.7.7-0.20251212092708-7bcfc87de402/go.mod h1:WYyUw//5yVw9BJnoVjx7rI/3szsISxNZCYGOqTIrV0o=
github.com/88250/pdfcpu v0.3.14-0.20250424122812-f10e8d9d8d46 h1:Bq1JsDfVbHKUxNL/B2JXd8cC/1h6aFjrlXpGycnh0Hk=
github.com/88250/pdfcpu v0.3.14-0.20250424122812-f10e8d9d8d46/go.mod h1:fVfOloBzs2+W2VJCCbq60XIxc3yJHAZ0Gahv1oO0gyI=
github.com/88250/vitess-sqlparser v0.0.0-20210205111146-56a2ded2aba1 h1:48T899JQDwyyRu9yXHePYlPdHtpJfrJEUGBMH3SMBWY=
@ -56,42 +56,42 @@ github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhP
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw=
github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef h1:2JGTg6JapxP9/R33ZaagQtAM4EkkSYnIAlOG5EI8gkM=
github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef/go.mod h1:JS7hed4L1fj0hXcyEejnW57/7LCetXggd+vwrRnYeII=
github.com/aws/aws-sdk-go-v2 v1.40.0 h1:/WMUA0kjhZExjOQN2z3oLALDREea1A7TobfuiBrKlwc=
github.com/aws/aws-sdk-go-v2 v1.40.0/go.mod h1:c9pm7VwuW0UPxAEYGyTmyurVcNrbF6Rt/wixFqDhcjE=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.3 h1:DHctwEM8P8iTXFxC/QK0MRjwEpWQeM9yzidCRjldUz0=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.3/go.mod h1:xdCzcZEtnSTKVDOmUZs4l/j3pSV6rpo1WXl5ugNsL8Y=
github.com/aws/aws-sdk-go-v2/config v1.32.2 h1:4liUsdEpUUPZs5WVapsJLx5NPmQhQdez7nYFcovrytk=
github.com/aws/aws-sdk-go-v2/config v1.32.2/go.mod h1:l0hs06IFz1eCT+jTacU/qZtC33nvcnLADAPL/XyrkZI=
github.com/aws/aws-sdk-go-v2/credentials v1.19.2 h1:qZry8VUyTK4VIo5aEdUcBjPZHL2v4FyQ3QEOaWcFLu4=
github.com/aws/aws-sdk-go-v2/credentials v1.19.2/go.mod h1:YUqm5a1/kBnoK+/NY5WEiMocZihKSo15/tJdmdXnM5g=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.14 h1:WZVR5DbDgxzA0BJeudId89Kmgy6DIU4ORpxwsVHz0qA=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.14/go.mod h1:Dadl9QO0kHgbrH1GRqGiZdYtW5w+IXXaBNCHTIaheM4=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.14 h1:PZHqQACxYb8mYgms4RZbhZG0a7dPW06xOjmaH0EJC/I=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.14/go.mod h1:VymhrMJUWs69D8u0/lZ7jSB6WgaG/NqHi3gX0aYf6U0=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.14 h1:bOS19y6zlJwagBfHxs0ESzr1XCOU2KXJCWcq3E2vfjY=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.14/go.mod h1:1ipeGBMAxZ0xcTm6y6paC2C/J6f6OO7LBODV9afuAyM=
github.com/aws/aws-sdk-go-v2 v1.40.1 h1:difXb4maDZkRH0x//Qkwcfpdg1XQVXEAEs2DdXldFFc=
github.com/aws/aws-sdk-go-v2 v1.40.1/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4 h1:489krEF9xIGkOaaX3CE/Be2uWjiXrkCH6gUX+bZA/BU=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4/go.mod h1:IOAPF6oT9KCsceNTvvYMNHy0+kMF8akOjeDvPENWxp4=
github.com/aws/aws-sdk-go-v2/config v1.32.3 h1:cpz7H2uMNTDa0h/5CYL5dLUEzPSLo2g0NkbxTRJtSSU=
github.com/aws/aws-sdk-go-v2/config v1.32.3/go.mod h1:srtPKaJJe3McW6T/+GMBZyIPc+SeqJsNPJsd4mOYZ6s=
github.com/aws/aws-sdk-go-v2/credentials v1.19.3 h1:01Ym72hK43hjwDeJUfi1l2oYLXBAOR8gNSZNmXmvuas=
github.com/aws/aws-sdk-go-v2/credentials v1.19.3/go.mod h1:55nWF/Sr9Zvls0bGnWkRxUdhzKqj9uRNlPvgV1vgxKc=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.15 h1:utxLraaifrSBkeyII9mIbVwXXWrZdlPO7FIKmyLCEcY=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.15/go.mod h1:hW6zjYUDQwfz3icf4g2O41PHi77u10oAzJ84iSzR/lo=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.15 h1:Y5YXgygXwDI5P4RkteB5yF7v35neH7LfJKBG+hzIons=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.15/go.mod h1:K+/1EpG42dFSY7CBj+Fruzm8PsCGWTXJ3jdeJ659oGQ=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.15 h1:AvltKnW9ewxX2hFmQS0FyJH93aSvJVUEFvXfU+HWtSE=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.15/go.mod h1:3I4oCdZdmgrREhU74qS1dK9yZ62yumob+58AbFR4cQA=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.14 h1:ITi7qiDSv/mSGDSWNpZ4k4Ve0DQR6Ug2SJQ8zEHoDXg=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.14/go.mod h1:k1xtME53H1b6YpZt74YmwlONMWf4ecM+lut1WQLAF/U=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3 h1:x2Ibm/Af8Fi+BH+Hsn9TXGdT+hKbDd5XOTZxTMxDk7o=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3/go.mod h1:IW1jwyrQgMdhisceG8fQLmQIydcT/jWY21rFhzgaKwo=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.5 h1:Hjkh7kE6D81PgrHlE/m9gx+4TyyeLHuY8xJs7yXN5C4=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.5/go.mod h1:nPRXgyCfAurhyaTMoBMwRBYBhaHI4lNPAnJmjM0Tslc=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.14 h1:FIouAnCE46kyYqyhs0XEBDFFSREtdnr8HQuLPQPLCrY=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.14/go.mod h1:UTwDc5COa5+guonQU8qBikJo1ZJ4ln2r1MkF7Dqag1E=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.14 h1:FzQE21lNtUor0Fb7QNgnEyiRCBlolLTX/Z1j65S7teM=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.14/go.mod h1:s1ydyWG9pm3ZwmmYN21HKyG9WzAZhYVW85wMHs5FV6w=
github.com/aws/aws-sdk-go-v2/service/s3 v1.92.1 h1:OgQy/+0+Kc3khtqiEOk23xQAglXi3Tj0y5doOxbi5tg=
github.com/aws/aws-sdk-go-v2/service/s3 v1.92.1/go.mod h1:wYNqY3L02Z3IgRYxOBPH9I1zD9Cjh9hI5QOy/eOjQvw=
github.com/aws/aws-sdk-go-v2/service/signin v1.0.2 h1:MxMBdKTYBjPQChlJhi4qlEueqB1p1KcbTEa7tD5aqPs=
github.com/aws/aws-sdk-go-v2/service/signin v1.0.2/go.mod h1:iS6EPmNeqCsGo+xQmXv0jIMjyYtQfnwg36zl2FwEouk=
github.com/aws/aws-sdk-go-v2/service/sso v1.30.5 h1:ksUT5KtgpZd3SAiFJNJ0AFEJVva3gjBmN7eXUZjzUwQ=
github.com/aws/aws-sdk-go-v2/service/sso v1.30.5/go.mod h1:av+ArJpoYf3pgyrj6tcehSFW+y9/QvAY8kMooR9bZCw=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.10 h1:GtsxyiF3Nd3JahRBJbxLCCdYW9ltGQYrFWg8XdkGDd8=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.10/go.mod h1:/j67Z5XBVDx8nZVp9EuFM9/BS5dvBznbqILGuu73hug=
github.com/aws/aws-sdk-go-v2/service/sts v1.41.2 h1:a5UTtD4mHBU3t0o6aHQZFJTNKVfxFWfPX7J0Lr7G+uY=
github.com/aws/aws-sdk-go-v2/service/sts v1.41.2/go.mod h1:6TxbXoDSgBQ225Qd8Q+MbxUxUh6TtNKwbRt/EPS9xso=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.15 h1:NLYTEyZmVZo0Qh183sC8nC+ydJXOOeIL/qI/sS3PdLY=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.15/go.mod h1:Z803iB3B0bc8oJV8zH2PERLRfQUJ2n2BXISpsA4+O1M=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 h1:0ryTNEdJbzUCEWkVXEXoqlXV72J5keC1GvILMOuD00E=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4/go.mod h1:HQ4qwNZh32C3CBeO6iJLQlgtMzqeG17ziAA/3KDJFow=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.6 h1:P1MU/SuhadGvg2jtviDXPEejU3jBNhoeeAlRadHzvHI=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.6/go.mod h1:5KYaMG6wmVKMFBSfWoyG/zH8pWwzQFnKgpoSRlXHKdQ=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.15 h1:3/u/4yZOffg5jdNk1sDpOQ4Y+R6Xbh+GzpDrSZjuy3U=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.15/go.mod h1:4Zkjq0FKjE78NKjabuM4tRXKFzUJWXgP0ItEZK8l7JU=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.15 h1:wsSQ4SVz5YE1crz0Ap7VBZrV4nNqZt4CIBBT8mnwoNc=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.15/go.mod h1:I7sditnFGtYMIqPRU1QoHZAUrXkGp4SczmlLwrNPlD0=
github.com/aws/aws-sdk-go-v2/service/s3 v1.93.0 h1:IrbE3B8O9pm3lsg96AXIN5MXX4pECEuExh/A0Du3AuI=
github.com/aws/aws-sdk-go-v2/service/s3 v1.93.0/go.mod h1:/sJLzHtiiZvs6C1RbxS/anSAFwZD6oC6M/kotQzOiLw=
github.com/aws/aws-sdk-go-v2/service/signin v1.0.3 h1:d/6xOGIllc/XW1lzG9a4AUBMmpLA9PXcQnVPTuHHcik=
github.com/aws/aws-sdk-go-v2/service/signin v1.0.3/go.mod h1:fQ7E7Qj9GiW8y0ClD7cUJk3Bz5Iw8wZkWDHsTe8vDKs=
github.com/aws/aws-sdk-go-v2/service/sso v1.30.6 h1:8sTTiw+9yuNXcfWeqKF2x01GqCF49CpP4Z9nKrrk/ts=
github.com/aws/aws-sdk-go-v2/service/sso v1.30.6/go.mod h1:8WYg+Y40Sn3X2hioaaWAAIngndR8n1XFdRPPX+7QBaM=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.11 h1:E+KqWoVsSrj1tJ6I/fjDIu5xoS2Zacuu1zT+H7KtiIk=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.11/go.mod h1:qyWHz+4lvkXcr3+PoGlGHEI+3DLLiU6/GdrFfMaAhB0=
github.com/aws/aws-sdk-go-v2/service/sts v1.41.3 h1:tzMkjh0yTChUqJDgGkcDdxvZDSrJ/WB6R6ymI5ehqJI=
github.com/aws/aws-sdk-go-v2/service/sts v1.41.3/go.mod h1:T270C0R5sZNLbWUe8ueiAF42XSZxxPocTaGSgs5c/60=
github.com/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk=
github.com/aws/smithy-go v1.24.0/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0=
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
@ -155,14 +155,14 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0=
github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/gabriel-vasile/mimetype v1.4.11 h1:AQvxbp830wPhHTqc1u7nzoLT+ZFxGY7emj5DR5DYFik=
github.com/gabriel-vasile/mimetype v1.4.11/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/gammazero/toposort v0.1.1 h1:OivGxsWxF3U3+U80VoLJ+f50HcPU1MIqE1JlKzoJ2Eg=
github.com/gammazero/toposort v0.1.1/go.mod h1:H2cozTnNpMw0hg2VHAYsAxmkHXBYroNangj2NTBQDvw=
github.com/gigawattio/window v0.0.0-20180317192513-0f5467e35573 h1:u8AQ9bPa9oC+8/A/jlWouakhIvkFfuxgIIRjiy8av7I=
github.com/gigawattio/window v0.0.0-20180317192513-0f5467e35573/go.mod h1:eBvb3i++NHDH4Ugo9qCvMw8t0mTSctaEa5blJbWcNxs=
github.com/gin-contrib/gzip v1.2.3 h1:dAhT722RuEG330ce2agAs75z7yB+NKvX/ZM1r8w0u2U=
github.com/gin-contrib/gzip v1.2.3/go.mod h1:ad72i4Bzmaypk8M762gNXa2wkxxjbz0icRNnuLJ9a/c=
github.com/gin-contrib/gzip v1.2.5 h1:fIZs0S+l17pIu1P5XRJOo/YNqfIuPCrZZ3TWB7pjckI=
github.com/gin-contrib/gzip v1.2.5/go.mod h1:aomRgR7ftdZV3uWY0gW/m8rChfxau0n8YVvwlOHONzw=
github.com/gin-contrib/sessions v1.0.4 h1:ha6CNdpYiTOK/hTp05miJLbpTSNfOnFg5Jm2kbcqy8U=
github.com/gin-contrib/sessions v1.0.4/go.mod h1:ccmkrb2z6iU2osiAHZG3x3J4suJK+OU27oqzlWOqQgs=
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
@ -360,8 +360,8 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/rqlite/sql v0.0.0-20251114131613-ef07423e7137 h1:OG5MfYAA0yaWgllfPdOq9Xa2bo1vpurCNx1LWz7+Zh0=
github.com/rqlite/sql v0.0.0-20251114131613-ef07423e7137/go.mod h1:ib9zVtNgRKiGuoMyUqqL5aNpk+r+++YlyiVIkclVqPg=
github.com/rqlite/sql v0.0.0-20251204023435-65660522892e h1:ccOm5zC6YqJtBrMmtiNcLPjFyWzB+TDY+fDIlQNsIFw=
github.com/rqlite/sql v0.0.0-20251204023435-65660522892e/go.mod h1:ib9zVtNgRKiGuoMyUqqL5aNpk+r+++YlyiVIkclVqPg=
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 h1:OkMGxebDjyw0ULyrTYWeN0UNCCkmCWfjPnIA2W6oviI=
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06/go.mod h1:+ePHsJ1keEjQtpvf9HHw0f4ZeJ0TLRsxhunSI2hYJSs=
github.com/sashabaranov/go-openai v1.41.2 h1:vfPRBZNMpnqu8ELsclWcAvF19lDNgh1t6TVfFFOPiSM=
@ -374,20 +374,20 @@ github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+D
github.com/shurcooL/gofontwoff v0.0.0-20181114050219-180f79e6909d h1:lvCTyBbr36+tqMccdGMwuEU+hjux/zL6xSmf5S9ITaA=
github.com/shurcooL/gofontwoff v0.0.0-20181114050219-180f79e6909d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
github.com/simplereach/timeutils v1.2.0/go.mod h1:VVbQDfN/FHRZa1LSqcwo4kNZ62OOyqLLGQKYB3pB0Q8=
github.com/siyuan-note/dataparser v0.0.0-20250804100744-b41253b236f3 h1:EH063L0HD1f82DvddurUmEXS0obXypv8pQrcaC/zNgI=
github.com/siyuan-note/dataparser v0.0.0-20250804100744-b41253b236f3/go.mod h1:8lb+SsWAPQblGbjmwEBsBdJszMCcLeECtB95fv6mReg=
github.com/siyuan-note/dejavu v0.0.0-20251202041457-7402b7c625c5 h1:KZ+l6WeLmdGobI193Ty/cqA15QOCOPBxvgKP9thFQf8=
github.com/siyuan-note/dejavu v0.0.0-20251202041457-7402b7c625c5/go.mod h1:NiyGdfe/v4QzmlCA9NLNfzkwHijB+Fr208f7WVOtMUE=
github.com/siyuan-note/dataparser v0.0.0-20251203120213-59c16535cb56 h1:aKzEVlOOSwvYqpbWTAQpGrpAZT8JrpAvaY9ek4PXIAQ=
github.com/siyuan-note/dataparser v0.0.0-20251203120213-59c16535cb56/go.mod h1:UGIytXL3Ge9iFj9RVDNAnfEOCPmzMPzpYTZyLtGC6tQ=
github.com/siyuan-note/dejavu v0.0.0-20251206130752-28126fa5ecd1 h1:lbHYhcOzS+WhfuCCmKgBt4erBM/RGemFtqVbSc52a7A=
github.com/siyuan-note/dejavu v0.0.0-20251206130752-28126fa5ecd1/go.mod h1:B+A9rUy7CTvf8ZvgHfAkI9oFuZY7Nk9DE5xm0sXrJ5g=
github.com/siyuan-note/encryption v0.0.0-20251120032857-3ddc3c2cc49f h1:HSgJKIAMgokJDAvBBfRj47SzRSm6mNGssY0Wv7rcEtg=
github.com/siyuan-note/encryption v0.0.0-20251120032857-3ddc3c2cc49f/go.mod h1:JE3S9VuJqTggyfhjesNDuqvqrRvwG3IctFjXXchLx1M=
github.com/siyuan-note/eventbus v0.0.0-20240627125516-396fdb0f0f97 h1:lM5v8BfNtbOL5jYwhCdMYBcYtr06IYBKjjSLAPMKTM8=
github.com/siyuan-note/eventbus v0.0.0-20240627125516-396fdb0f0f97/go.mod h1:1/nGgthl89FPA7GzAcEWKl6zRRnfgyTjzLZj9bW7kuw=
github.com/siyuan-note/filelock v0.0.0-20251107023958-207cad31f0dd h1:7Rr+wH2hy/goTMe5EAZHjpfEudZZXVVaZOkhs/xzX+I=
github.com/siyuan-note/filelock v0.0.0-20251107023958-207cad31f0dd/go.mod h1:jFSEENJoE+SRZ7xtSpiflL3RMfnxE6CAJFUfBuuGs1A=
github.com/siyuan-note/filelock v0.0.0-20251212095217-08318833e008 h1:3wEmNS4eZkxwm1rhXDhVK5Y0o/GKAZtfe1VV584BF+A=
github.com/siyuan-note/filelock v0.0.0-20251212095217-08318833e008/go.mod h1:9OhXAyOkSXwuLvNCZk2aFMo0nOldyO3f2hMJEnkuT30=
github.com/siyuan-note/httpclient v0.0.0-20251119144307-63b815d7d198 h1:NCFtk65n+a6oo+nIXnYPx3jCgs3O2uvvihnD7aRy/ZE=
github.com/siyuan-note/httpclient v0.0.0-20251119144307-63b815d7d198/go.mod h1:w8ZKhKvcOr6lXsfxGmXQQTcIlVYWKhesB5IaXC+3OkA=
github.com/siyuan-note/logging v0.0.0-20251107023700-cd4339891032 h1:z1r9ZhSTISDom1lvqKuiykfI7YtPCIELABj8onc0n3A=
github.com/siyuan-note/logging v0.0.0-20251107023700-cd4339891032/go.mod h1:UVFePdmdasN+fLDEYFr2X734G2AIJb6nYS9WcWK5740=
github.com/siyuan-note/logging v0.0.0-20251209020516-52f1a2f65ec5 h1:bIMoJAAf3tV0xYcN+N2Vw7Ot/LbVxuz715o1rn1GDto=
github.com/siyuan-note/logging v0.0.0-20251209020516-52f1a2f65ec5/go.mod h1:U6DyWKvtIPW9WrUoUikPCwFUzUoHGtEJjjeLNYae1nc=
github.com/siyuan-note/riff v0.0.0-20251022131846-228528e70754 h1:6QYpy7s5HlRSge09TyM/mT0vz1RDcWYZdkxEh7hmbH4=
github.com/siyuan-note/riff v0.0.0-20251022131846-228528e70754/go.mod h1:/N7+N2CsZ0nleNPpP3b+06Bzqvuhy6GUmLY7Kug/zT0=
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
@ -451,8 +451,8 @@ go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/arch v0.22.0 h1:c/Zle32i5ttqRXjdLyyHZESLD/bB90DCU1g9l/0YBDI=
golang.org/x/arch v0.22.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg=
golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
@ -461,21 +461,21 @@ golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
golang.org/x/image v0.32.0 h1:6lZQWq75h7L5IWNk0r+SCpUJ6tUVd3v4ZHnbRKLkUDQ=
golang.org/x/image v0.32.0/go.mod h1:/R37rrQmKXtO6tYXAjtDLwQgFLHmhW+V6ayXlxzP2Pc=
golang.org/x/mobile v0.0.0-20251009145931-8baca8bf4eeb h1:6lzmAebw71+I8PM7W9A/VomU3XWEwZkkwp9Jh4XJX7c=
golang.org/x/mobile v0.0.0-20251009145931-8baca8bf4eeb/go.mod h1:3QSlP0AtP6HPTLbsxfgfefGN76jpIB9yBsMqB8UY37I=
golang.org/x/image v0.33.0 h1:LXRZRnv1+zGd5XBUVRFmYEphyyKJjQjCRiOuAP3sZfQ=
golang.org/x/image v0.33.0/go.mod h1:DD3OsTYT9chzuzTQt+zMcOlBHgfoKQb1gry8p76Y1sc=
golang.org/x/mobile v0.0.0-20251126181937-5c265dc024c4 h1:lZKReZrCBTDNaVewUp31194cua6qf65/tYg3mq1KUU0=
golang.org/x/mobile v0.0.0-20251126181937-5c265dc024c4/go.mod h1:Eq3Nh/5pFSWug2ohiudJ1iyU59SO78QFuh4qTTN++I0=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -490,8 +490,8 @@ golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -499,8 +499,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -519,8 +519,8 @@ golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@ -544,8 +544,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -554,8 +554,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=

View file

@ -680,8 +680,10 @@ func RemoveUnusedAssets() (ret []string) {
}
}
if err := filelock.Remove(absPath); err != nil {
logging.LogErrorf("remove unused asset [%s] failed: %s", absPath, err)
if removeErr := filelock.RemoveWithoutFatal(absPath); removeErr != nil {
logging.LogErrorf("remove unused asset [%s] failed: %s", absPath, removeErr)
util.PushErrMsg(fmt.Sprintf("%s", removeErr), 7000)
return
}
util.RemoveAssetText(unusedAsset)
}
@ -720,8 +722,10 @@ func RemoveUnusedAsset(p string) (ret string) {
cache.RemoveAssetHash(hash)
}
if err = filelock.Remove(absPath); err != nil {
if err = filelock.RemoveWithoutFatal(absPath); err != nil {
logging.LogErrorf("remove unused asset [%s] failed: %s", absPath, err)
util.PushErrMsg(fmt.Sprintf("%s", err), 7000)
return
}
ret = absPath

View file

@ -241,6 +241,8 @@ func getAttrViewAddingBlockDefaultValues(attrView *av.AttributeView, view, group
if !av.MSelectExistOption(newValue.MSelect, groupView.GetGroupValue()) {
if 1 > len(newValue.MSelect) || av.KeyTypeMSelect == groupKey.Type {
newValue.MSelect = append(newValue.MSelect, &av.ValueSelect{Content: opt.Name, Color: opt.Color})
} else {
newValue.MSelect = []*av.ValueSelect{{Content: opt.Name, Color: opt.Color}}
}
} else {
var vals []*av.ValueSelect
@ -3987,11 +3989,18 @@ func sortAttributeViewRow(operation *Operation) (err error) {
if targetGroupView := view.GetGroupByID(operation.TargetGroupID); nil != targetGroupView && !gulu.Str.Contains(itemID, targetGroupView.GroupItemIDs) {
fillDefaultValue(attrView, view, targetGroupView, operation.PreviousID, itemID, false)
// 移除旧分组的值
if val := attrView.GetValue(groupKey.ID, itemID); nil != val {
if av.MSelectExistOption(val.MSelect, groupView.GetGroupValue()) {
// 移除旧分组的值
val.MSelect = av.MSelectRemoveOption(val.MSelect, groupView.GetGroupValue())
}
now := time.Now().UnixMilli()
val.SetUpdatedAt(now)
if blockVal := attrView.GetBlockValue(itemID); nil != blockVal {
blockVal.Block.Updated = now
blockVal.SetUpdatedAt(now)
}
}
for i, r := range targetGroupView.GroupItemIDs {

View file

@ -256,8 +256,8 @@ func UninstallBazaarPlugin(pluginName, frontend string) error {
petals = tmp
savePetals(petals)
removePluginSet := hashset.New(pluginName)
PushReloadPlugin(nil, nil, removePluginSet, "")
uninstallPluginSet := hashset.New(pluginName)
PushReloadPlugin(nil, nil, nil, uninstallPluginSet, "")
return nil
}

View file

@ -212,7 +212,7 @@ func GetBlockSiblingID(id string) (parent, previous, next string) {
parent = parentBlock.ID
if ast.NodeDocument == parentBlock.Type {
parent = parentBlock.ID
parent = treenode.FirstLeafBlock(parentBlock).ID
if nil != current.Previous && current.Previous.IsBlock() {
previous = current.Previous.ID
@ -230,24 +230,37 @@ func GetBlockSiblingID(id string) (parent, previous, next string) {
return
}
parentCount := 0
for ; nil != parentBlock; parentBlock = treenode.ParentBlock(parentBlock) {
if nil != parentBlock.Previous && parentBlock.Previous.IsBlock() {
previous = parentBlock.Previous.ID
if flb := treenode.FirstChildBlock(parentBlock.Previous); nil != flb {
previous = flb.ID
}
if ast.NodeDocument == parentBlock.Type {
break
}
if ast.NodeList == parentBlock.Type || ast.NodeBlockquote == parentBlock.Type || ast.NodeSuperBlock == parentBlock.Type || ast.NodeCallout == parentBlock.Type {
parentCount++
continue
}
if ast.NodeListItem == parentBlock.Type {
if 1 > parentCount {
parentBlock = treenode.ParentBlock(parentBlock)
}
parentBlock = treenode.ParentBlock(parentBlock)
}
break
}
parentBlock = treenode.ParentBlock(current)
for ; nil != parentBlock; parentBlock = treenode.ParentBlock(parentBlock) {
if nil != parentBlock.Next && parentBlock.Next.IsBlock() {
next = parentBlock.Next.ID
if flb := treenode.FirstChildBlock(parentBlock.Next); nil != flb {
next = flb.ID
}
break
}
if ast.NodeDocument == parentBlock.Type {
parentBlock = treenode.FirstLeafBlock(parentBlock)
parent = parentBlock.ID
} else {
parent = parentBlock.ID
}
if nil != parentBlock.Previous {
previous = parentBlock.Previous.ID
}
if nil != parentBlock.Next {
next = parentBlock.Next.ID
}
return
}

View file

@ -532,7 +532,7 @@ func buildBlockBreadcrumb(node *ast.Node, excludeTypes []string, isEmbedBlock bo
name, _ = av.GetAttributeViewName(parent.AttributeViewID)
} else {
if "" == name {
if ast.NodeListItem == parent.Type || ast.NodeList == parent.Type || ast.NodeSuperBlock == parent.Type || ast.NodeBlockquote == parent.Type {
if ast.NodeListItem == parent.Type || ast.NodeList == parent.Type || ast.NodeSuperBlock == parent.Type || ast.NodeBlockquote == parent.Type || ast.NodeCallout == parent.Type {
name = gulu.Str.SubStr(renderBlockText(fc, excludeTypes, true), maxNameLen)
} else {
name = gulu.Str.SubStr(renderBlockText(parent, excludeTypes, true), maxNameLen)
@ -544,7 +544,7 @@ func buildBlockBreadcrumb(node *ast.Node, excludeTypes []string, isEmbedBlock bo
}
add := true
if ast.NodeList == parent.Type || ast.NodeSuperBlock == parent.Type || ast.NodeBlockquote == parent.Type {
if ast.NodeList == parent.Type || ast.NodeSuperBlock == parent.Type || ast.NodeBlockquote == parent.Type || ast.NodeCallout == parent.Type {
add = false
if parent == node {
// https://github.com/siyuan-note/siyuan/issues/13141#issuecomment-2476789553

View file

@ -539,7 +539,7 @@ func normalizeTree(tree *parse.Tree) (yfmRootID, yfmTitle, yfmUpdated string) {
if "" == n.IALAttr("id") && (ast.NodeParagraph == n.Type || ast.NodeList == n.Type || ast.NodeListItem == n.Type || ast.NodeBlockquote == n.Type ||
ast.NodeMathBlock == n.Type || ast.NodeCodeBlock == n.Type || ast.NodeHeading == n.Type || ast.NodeTable == n.Type || ast.NodeThematicBreak == n.Type ||
ast.NodeYamlFrontMatter == n.Type || ast.NodeBlockQueryEmbed == n.Type || ast.NodeSuperBlock == n.Type || ast.NodeAttributeView == n.Type ||
ast.NodeHTMLBlock == n.Type || ast.NodeIFrame == n.Type || ast.NodeWidget == n.Type || ast.NodeAudio == n.Type || ast.NodeVideo == n.Type) {
ast.NodeHTMLBlock == n.Type || ast.NodeIFrame == n.Type || ast.NodeWidget == n.Type || ast.NodeAudio == n.Type || ast.NodeVideo == n.Type || ast.NodeCallout == n.Type) {
n.ID = ast.NewNodeID()
n.KramdownIAL = [][]string{{"id", n.ID}}
n.InsertAfter(&ast.Node{Type: ast.NodeKramdownBlockIAL, Tokens: []byte("{: id=\"" + n.ID + "\"}")})

View file

@ -240,8 +240,8 @@ func InitConf() {
if nil == Conf.Editor {
Conf.Editor = defaultEditor
}
// 新增字段的默认值
// 使用指针类型来区分字段不存在nil和用户设置为 0非 nil
// 新增字段的默认值,使用指针类型来区分字段不存在nil和用户设置为 0非 nil
if nil == Conf.Editor.BacklinkSort {
Conf.Editor.BacklinkSort = defaultEditor.BacklinkSort
}
@ -282,6 +282,9 @@ func InitConf() {
if conf.MinDynamicLoadBlocks > Conf.Editor.DynamicLoadBlocks {
Conf.Editor.DynamicLoadBlocks = conf.MinDynamicLoadBlocks
}
if 1 > len(Conf.Editor.SpellcheckLanguages) {
Conf.Editor.SpellcheckLanguages = []string{"en-US"}
}
if 0 > Conf.Editor.BacklinkExpandCount {
Conf.Editor.BacklinkExpandCount = 0
}
@ -742,7 +745,15 @@ func Close(force, setCurrentWorkspace bool, execInstallPkg int) (exitCode int) {
if nil != util.WebSocketServer {
util.WebSocketServer.Close()
}
if nil != util.HttpServer {
util.HttpServer.Close()
}
util.HttpServing = false
if util.ContainerAndroid == util.Container || util.ContainerIOS == util.Container || util.ContainerHarmony == util.Container {
return
}
os.Exit(logging.ExitCodeOk)
}()
return

View file

@ -586,7 +586,7 @@ func ExportResources(resourcePaths []string, mainName string) (exportFilePath st
return
}
func Preview(id string, fillCSSVar bool) (retStdHTML string) {
func ExportPreview(id string, fillCSSVar bool) (retStdHTML string) {
blockRefMode := Conf.Export.BlockRefMode
bt := treenode.GetBlockTree(id)
if nil == bt {
@ -891,6 +891,9 @@ func ExportHTML(id, savePath string, pdf, image, keepFold, merge bool) (name, do
tree := prepareExportTree(bt)
node = treenode.GetNodeInTree(tree, id)
if ast.NodeDocument == node.Type {
node.RemoveIALAttr("style")
}
if merge {
var mergeErr error
@ -905,7 +908,7 @@ func ExportHTML(id, savePath string, pdf, image, keepFold, merge bool) (name, do
var headings []*ast.Node
if pdf { // 导出 PDF 需要标记目录书签
ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
if entering && ast.NodeHeading == n.Type && !n.ParentIs(ast.NodeBlockquote) {
if entering && ast.NodeHeading == n.Type && !n.ParentIs(ast.NodeBlockquote) && !n.ParentIs(ast.NodeCallout) {
headings = append(headings, n)
return ast.WalkSkipChildren
}
@ -1128,7 +1131,7 @@ func ProcessPDF(id, p string, merge, removeAssets, watermark bool) (err error) {
return ast.WalkContinue
}
if ast.NodeHeading == n.Type && !n.ParentIs(ast.NodeBlockquote) {
if ast.NodeHeading == n.Type && !n.ParentIs(ast.NodeBlockquote) && !n.ParentIs(ast.NodeCallout) {
headings = append(headings, n)
return ast.WalkSkipChildren
}
@ -2442,7 +2445,7 @@ func exportTree(tree *parse.Tree, wysiwyg, keepFold, avHiddenCol bool,
root.IAL["type"] = "doc"
title := &ast.Node{Type: ast.NodeHeading, HeadingLevel: 1}
for k, v := range root.IAL {
if "type" == k {
if "type" == k || "style" == k {
continue
}
title.SetIALAttr(k, v)
@ -3557,14 +3560,14 @@ func adjustHeadingLevel(bt *treenode.BlockTree, tree *parse.Tree) {
var firstHeading *ast.Node
if !Conf.Export.AddTitle {
for n := tree.Root.FirstChild; nil != n; n = n.Next {
if ast.NodeHeading == n.Type && !n.ParentIs(ast.NodeBlockquote) {
if ast.NodeHeading == n.Type && !n.ParentIs(ast.NodeBlockquote) && !n.ParentIs(ast.NodeCallout) {
firstHeading = n
break
}
}
} else {
for n := tree.Root.FirstChild.Next; nil != n; n = n.Next {
if ast.NodeHeading == n.Type && !n.ParentIs(ast.NodeBlockquote) {
if ast.NodeHeading == n.Type && !n.ParentIs(ast.NodeBlockquote) && !n.ParentIs(ast.NodeCallout) {
firstHeading = n
break
}

View file

@ -187,8 +187,8 @@ func SearchDocsByKeyword(keyword string, flashcard bool, excludeIDs []string) (r
}
}
if 0 < len(excludeIDs) {
condition += fmt.Sprintf(" AND root_id NOT IN ('%s')", strings.Join(excludeIDs, "', '"))
for _, excludeID := range excludeIDs {
condition += fmt.Sprintf(" AND path NOT LIKE '%%%s%%' ", excludeID)
}
rootBlocks = sql.QueryRootBlockByCondition(condition, Conf.Search.Limit)
@ -978,12 +978,6 @@ func DuplicateDoc(tree *parse.Tree) {
previousPath := tree.Path
resetTree(tree, "Duplicated", false)
createTreeTx(tree)
box := Conf.Box(tree.Box)
if nil != box {
box.addSort(previousPath, tree.ID)
}
FlushTxQueue()
// 复制为副本时移除数据库绑定状态 https://github.com/siyuan-note/siyuan/issues/12294
ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
@ -996,6 +990,13 @@ func DuplicateDoc(tree *parse.Tree) {
n.RemoveIALAttrsByPrefix(av.NodeAttrViewStaticText)
return ast.WalkContinue
})
createTreeTx(tree)
box := Conf.Box(tree.Box)
if nil != box {
box.addSort(previousPath, tree.ID)
}
FlushTxQueue()
return
}
@ -1748,7 +1749,7 @@ func createDoc(boxID, p, title, dom string) (tree *parse.Tree, err error) {
tree.HPath = hPath
tree.ID = id
tree.Root.ID = id
tree.Root.Spec = "1"
tree.Root.Spec = treenode.CurrentSpec
updated := util.TimeFromID(id)
tree.Root.KramdownIAL = [][]string{{"id", id}, {"title", html.EscapeAttrVal(title)}, {"updated", updated}}
if nil == tree.Root.FirstChild {

View file

@ -23,6 +23,7 @@ import (
"github.com/88250/lute/editor"
"github.com/88250/lute/render"
"github.com/siyuan-note/logging"
"github.com/siyuan-note/siyuan/kernel/treenode"
"github.com/siyuan-note/siyuan/kernel/util"
)
@ -63,13 +64,13 @@ func AutoSpace(rootID string) (err error) {
formatRenderer := render.NewFormatRenderer(tree, luteEngine.RenderOptions)
md := formatRenderer.Render()
newTree := parseKTree(md)
newTree.Root.Spec = "1"
newTree.Root.Spec = treenode.CurrentSpec
// 第二次格式化启用自动空格
luteEngine.SetAutoSpace(true)
formatRenderer = render.NewFormatRenderer(newTree, luteEngine.RenderOptions)
md = formatRenderer.Render()
newTree = parseKTree(md)
newTree.Root.Spec = "1"
newTree.Root.Spec = treenode.CurrentSpec
newTree.Root.ID = tree.ID
newTree.Root.KramdownIAL = rootIAL
newTree.ID = tree.ID

View file

@ -596,6 +596,14 @@ func graphTypeFilter(local bool) string {
inList = append(inList, "'s'")
}
callout := Conf.Graph.Local.Callout
if !local {
callout = Conf.Graph.Global.Callout
}
if callout {
inList = append(inList, "'callout'")
}
inList = append(inList, "'d'")
return " AND ref.type IN (" + strings.Join(inList, ",") + ")"
}

View file

@ -427,7 +427,7 @@ func Heading2Doc(srcHeadingID, targetBoxID, targetPath, previousPath string) (sr
newTree.Box, newTree.Path = targetBoxID, newTargetPath
newTree.Root.SetIALAttr("updated", util.CurrentTimeSecondsStr())
newTree.Root.Spec = "1"
newTree.Root.Spec = treenode.CurrentSpec
if "" != previousPath {
box.addSort(previousPath, newTree.ID)
} else {

View file

@ -54,11 +54,11 @@ func AutoGenerateFileHistory() {
ChangeHistoryTick(Conf.Editor.GenerateHistoryInterval)
for {
<-historyTicker.C
task.AppendTask(task.HistoryGenerateFile, generateFileHistory)
task.AppendTask(task.HistoryGenerateFile, GenerateFileHistory)
}
}
func generateFileHistory() {
func GenerateFileHistory() {
defer logging.Recover()
if 1 > Conf.Editor.GenerateHistoryInterval {

View file

@ -418,10 +418,7 @@ func ImportSY(zipPath, boxID, toPath string) (err error) {
for _, tree := range trees {
util.PushEndlessProgress(Conf.language(73) + " " + fmt.Sprintf(Conf.language(70), tree.Root.IALAttr("title")))
syPath := filepath.Join(unzipRootPath, tree.Path)
if "" == tree.Root.Spec {
parse.NestedInlines2FlattedSpans(tree, false)
tree.Root.Spec = "1"
}
treenode.UpgradeSpec(tree)
renderer := render.NewJSONRenderer(tree, luteEngine.RenderOptions)
data := renderer.Render()
@ -943,7 +940,7 @@ func ImportFromLocalPath(boxID, localPath string, toPath string) (err error) {
tree.Path = targetPath
targetPaths[curRelPath] = targetPath
tree.HPath = hPath
tree.Root.Spec = "1"
tree.Root.Spec = treenode.CurrentSpec
docDirLocalPath := filepath.Dir(filepath.Join(boxLocalPath, targetPath))
assetDirPath := getAssetsDir(boxLocalPath, docDirLocalPath)
@ -1075,7 +1072,7 @@ func ImportFromLocalPath(boxID, localPath string, toPath string) (err error) {
tree.Box = boxID
tree.Path = targetPath
tree.HPath = path.Join(baseHPath, title)
tree.Root.Spec = "1"
tree.Root.Spec = treenode.CurrentSpec
docDirLocalPath := filepath.Dir(filepath.Join(boxLocalPath, targetPath))
assetDirPath := getAssetsDir(boxLocalPath, docDirLocalPath)

View file

@ -128,7 +128,7 @@ func ListItem2Doc(srcListItemID, targetBoxID, targetPath, previousPath string) (
newTree.Box, newTree.Path = targetBoxID, newTargetPath
newTree.Root.SetIALAttr("updated", util.CurrentTimeSecondsStr())
newTree.Root.Spec = "1"
newTree.Root.Spec = treenode.CurrentSpec
if "" != previousPath {
box.addSort(previousPath, newTree.ID)
} else {

View file

@ -54,7 +54,7 @@ func (tx *Transaction) doMoveOutlineHeading(operation *Operation) (ret *TxErr) {
headings := []*ast.Node{}
ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
if entering && ast.NodeHeading == n.Type && !n.ParentIs(ast.NodeBlockquote) {
if entering && ast.NodeHeading == n.Type && !n.ParentIs(ast.NodeBlockquote) && !n.ParentIs(ast.NodeCallout) {
headings = append(headings, n)
}
return ast.WalkContinue
@ -305,7 +305,7 @@ func outline(tree *parse.Tree) (ret []*Path) {
luteEngine := NewLute()
var headings []*Block
ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
if entering && ast.NodeHeading == n.Type && !n.ParentIs(ast.NodeBlockquote) {
if entering && ast.NodeHeading == n.Type && !n.ParentIs(ast.NodeBlockquote) && !n.ParentIs(ast.NodeCallout) {
n.Box, n.Path = tree.Box, tree.Path
block := &Block{
RootID: tree.Root.ID,

View file

@ -38,11 +38,25 @@ import (
"github.com/siyuan-note/siyuan/kernel/util"
)
func PushReloadPlugin(upsertCodePluginSet, upsertDataPluginSet, removePluginNameSet *hashset.Set, excludeApp string) {
if nil != removePluginNameSet {
for _, n := range removePluginNameSet.Values() {
func PushReloadPlugin(upsertCodePluginSet, upsertDataPluginSet, unloadPluginNameSet, uninstallPluginNameSet *hashset.Set, excludeApp string) {
// 集合去重
if nil != uninstallPluginNameSet {
for _, n := range uninstallPluginNameSet.Values() {
pluginName := n.(string)
if nil != upsertCodePluginSet {
upsertCodePluginSet.Remove(pluginName)
}
if nil != upsertDataPluginSet {
upsertDataPluginSet.Remove(pluginName)
}
if nil != unloadPluginNameSet {
unloadPluginNameSet.Remove(pluginName)
}
}
}
if nil != unloadPluginNameSet {
for _, n := range unloadPluginNameSet.Values() {
pluginName := n.(string)
// 如果插件在 removePluginSet 中,从其他集合中移除
if nil != upsertCodePluginSet {
upsertCodePluginSet.Remove(pluginName)
}
@ -54,14 +68,13 @@ func PushReloadPlugin(upsertCodePluginSet, upsertDataPluginSet, removePluginName
if nil != upsertCodePluginSet {
for _, n := range upsertCodePluginSet.Values() {
pluginName := n.(string)
// 如果插件在 upsertCodePluginSet 中,从 upsertDataPluginSet 中移除
if nil != upsertDataPluginSet {
upsertDataPluginSet.Remove(pluginName)
}
}
}
upsertCodePlugins, upsertDataPlugins, removePlugins := []string{}, []string{}, []string{}
upsertCodePlugins, upsertDataPlugins, unloadPlugins, uninstallPlugins := []string{}, []string{}, []string{}, []string{}
if nil != upsertCodePluginSet {
for _, n := range upsertCodePluginSet.Values() {
upsertCodePlugins = append(upsertCodePlugins, n.(string))
@ -72,22 +85,28 @@ func PushReloadPlugin(upsertCodePluginSet, upsertDataPluginSet, removePluginName
upsertDataPlugins = append(upsertDataPlugins, n.(string))
}
}
if nil != removePluginNameSet {
for _, n := range removePluginNameSet.Values() {
removePlugins = append(removePlugins, n.(string))
if nil != unloadPluginNameSet {
for _, n := range unloadPluginNameSet.Values() {
unloadPlugins = append(unloadPlugins, n.(string))
}
}
if nil != uninstallPluginNameSet {
for _, n := range uninstallPluginNameSet.Values() {
uninstallPlugins = append(uninstallPlugins, n.(string))
}
}
pushReloadPlugin0(upsertCodePlugins, upsertDataPlugins, removePlugins, excludeApp)
pushReloadPlugin0(upsertCodePlugins, upsertDataPlugins, unloadPlugins, uninstallPlugins, excludeApp)
}
func pushReloadPlugin0(upsertCodePlugins, upsertDataPlugins, removePlugins []string, excludeApp string) {
logging.LogInfof("reload plugins [codeChanges=%v, dataChanges=%v, removes=%v]", upsertCodePlugins, upsertDataPlugins, removePlugins)
func pushReloadPlugin0(upsertCodePlugins, upsertDataPlugins, unloadPlugins, uninstallPlugins []string, excludeApp string) {
logging.LogInfof("reload plugins [codeChanges=%v, dataChanges=%v, unloads=%v, uninstalls=%v]", upsertCodePlugins, upsertDataPlugins, unloadPlugins, uninstallPlugins)
if "" == excludeApp {
util.BroadcastByType("main", "reloadPlugin", 0, "", map[string]interface{}{
"upsertCodePlugins": upsertCodePlugins,
"upsertDataPlugins": upsertDataPlugins,
"removePlugins": removePlugins,
"unloadPlugins": unloadPlugins,
"uninstallPlugins": uninstallPlugins,
})
return
}
@ -95,7 +114,8 @@ func pushReloadPlugin0(upsertCodePlugins, upsertDataPlugins, removePlugins []str
util.BroadcastByTypeAndExcludeApp(excludeApp, "main", "reloadPlugin", 0, "", map[string]interface{}{
"upsertCodePlugins": upsertCodePlugins,
"upsertDataPlugins": upsertDataPlugins,
"removePlugins": removePlugins,
"unloadPlugins": unloadPlugins,
"uninstallPlugins": uninstallPlugins,
})
}

View file

@ -256,165 +256,168 @@ func resolveEmbedR(n *ast.Node, blockEmbedMode int, luteEngine *lute.Lute, resol
return ast.WalkContinue
}
if ast.NodeBlockQueryEmbed == n.Type {
if gulu.Str.Contains(n.ID, *resolved) {
return ast.WalkContinue
if ast.NodeBlockQueryEmbed != n.Type {
return ast.WalkContinue
}
if gulu.Str.Contains(n.ID, *resolved) {
return ast.WalkContinue
}
*resolved = append(*resolved, n.ID)
stmt := n.ChildByType(ast.NodeBlockQueryEmbedScript).TokensStr()
stmt = html.UnescapeString(stmt)
stmt = strings.ReplaceAll(stmt, editor.IALValEscNewLine, "\n")
sqlBlocks := sql.SelectBlocksRawStmt(stmt, 1, Conf.Search.Limit)
for _, sqlBlock := range sqlBlocks {
if "query_embed" == sqlBlock.Type {
continue
}
*resolved = append(*resolved, n.ID)
stmt := n.ChildByType(ast.NodeBlockQueryEmbedScript).TokensStr()
stmt = html.UnescapeString(stmt)
stmt = strings.ReplaceAll(stmt, editor.IALValEscNewLine, "\n")
sqlBlocks := sql.SelectBlocksRawStmt(stmt, 1, Conf.Search.Limit)
for _, sqlBlock := range sqlBlocks {
if "query_embed" == sqlBlock.Type {
continue
}
subTree, _ := LoadTreeByBlockID(sqlBlock.ID)
if nil == subTree {
continue
}
subTree, _ := LoadTreeByBlockID(sqlBlock.ID)
if nil == subTree {
continue
}
var md string
if "d" == sqlBlock.Type {
if 0 == blockEmbedMode {
// 嵌入块中出现了大于等于上方非嵌入块的标题时需要降低嵌入块中的标题级别
// Improve export of heading levels in embedded blocks https://github.com/siyuan-note/siyuan/issues/12233 https://github.com/siyuan-note/siyuan/issues/12741
embedTopLevel := 0
ast.Walk(subTree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
if !entering || ast.NodeHeading != n.Type {
return ast.WalkContinue
}
embedTopLevel = n.HeadingLevel
if parentHeadingLevel >= embedTopLevel {
n.HeadingLevel += parentHeadingLevel - embedTopLevel + 1
if 6 < n.HeadingLevel {
n.HeadingLevel = 6
}
}
var md string
if "d" == sqlBlock.Type {
if 0 == blockEmbedMode {
// 嵌入块中出现了大于等于上方非嵌入块的标题时需要降低嵌入块中的标题级别
// Improve export of heading levels in embedded blocks https://github.com/siyuan-note/siyuan/issues/12233 https://github.com/siyuan-note/siyuan/issues/12741
embedTopLevel := 0
ast.Walk(subTree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
if !entering || ast.NodeHeading != n.Type {
return ast.WalkContinue
})
}
md, _ = lute.FormatNodeSync(subTree.Root, luteEngine.ParseOptions, luteEngine.RenderOptions)
} else if "h" == sqlBlock.Type {
h := treenode.GetNodeInTree(subTree, sqlBlock.ID)
var hChildren []*ast.Node
// 从嵌入块的 IAL 属性中解析 custom-heading-mode使用全局配置作为默认值
blockHeadingMode := Conf.Editor.HeadingEmbedMode
if customHeadingMode := n.IALAttr("custom-heading-mode"); "" != customHeadingMode {
if mode, err := strconv.Atoi(customHeadingMode); nil == err && (mode == 0 || mode == 1 || mode == 2) {
blockHeadingMode = mode
}
}
// 根据 blockHeadingMode 处理标题块的显示
// blockHeadingMode: 0=显示标题与下方的块1=仅显示标题2=仅显示标题下方的块
if 1 == blockHeadingMode {
// 仅显示标题
hChildren = append(hChildren, h)
} else if 2 == blockHeadingMode {
// 仅显示标题下方的块(默认行为)
if "1" != h.IALAttr("fold") {
children := treenode.HeadingChildren(h)
for _, c := range children {
if "1" == c.IALAttr("heading-fold") {
// 嵌入块包含折叠标题时不应该显示其下方块 https://github.com/siyuan-note/siyuan/issues/4765
continue
}
hChildren = append(hChildren, c)
embedTopLevel = n.HeadingLevel
if parentHeadingLevel >= embedTopLevel {
n.HeadingLevel += parentHeadingLevel - embedTopLevel + 1
if 6 < n.HeadingLevel {
n.HeadingLevel = 6
}
}
} else {
// 0: 显示标题与下方的块
hChildren = append(hChildren, h)
hChildren = append(hChildren, treenode.HeadingChildren(h)...)
return ast.WalkContinue
})
}
md, _ = lute.FormatNodeSync(subTree.Root, luteEngine.ParseOptions, luteEngine.RenderOptions)
} else if "h" == sqlBlock.Type {
h := treenode.GetNodeInTree(subTree, sqlBlock.ID)
var hChildren []*ast.Node
// 从嵌入块的 IAL 属性中解析 custom-heading-mode使用全局配置作为默认值
blockHeadingMode := Conf.Editor.HeadingEmbedMode
if customHeadingMode := n.IALAttr("custom-heading-mode"); "" != customHeadingMode {
if mode, err := strconv.Atoi(customHeadingMode); nil == err && (mode == 0 || mode == 1 || mode == 2) {
blockHeadingMode = mode
}
if 0 == blockEmbedMode {
embedTopLevel := 0
}
// 根据 blockHeadingMode 处理标题块的显示
// blockHeadingMode: 0=显示标题与下方的块1=仅显示标题2=仅显示标题下方的块
if 1 == blockHeadingMode {
// 仅显示标题
hChildren = append(hChildren, h)
} else if 2 == blockHeadingMode {
// 仅显示标题下方的块(默认行为)
if "1" != h.IALAttr("fold") {
children := treenode.HeadingChildren(h)
for _, c := range children {
if "1" == c.IALAttr("heading-fold") {
// 嵌入块包含折叠标题时不应该显示其下方块 https://github.com/siyuan-note/siyuan/issues/4765
continue
}
hChildren = append(hChildren, c)
}
}
} else {
// 0: 显示标题与下方的块
hChildren = append(hChildren, h)
hChildren = append(hChildren, treenode.HeadingChildren(h)...)
}
if 0 == blockEmbedMode {
embedTopLevel := 0
for _, hChild := range hChildren {
if ast.NodeHeading == hChild.Type {
embedTopLevel = hChild.HeadingLevel
break
}
}
if parentHeadingLevel >= embedTopLevel {
for _, hChild := range hChildren {
if ast.NodeHeading == hChild.Type {
embedTopLevel = hChild.HeadingLevel
break
}
}
if parentHeadingLevel >= embedTopLevel {
for _, hChild := range hChildren {
if ast.NodeHeading == hChild.Type {
hChild.HeadingLevel += parentHeadingLevel - embedTopLevel + 1
if 6 < hChild.HeadingLevel {
hChild.HeadingLevel = 6
}
hChild.HeadingLevel += parentHeadingLevel - embedTopLevel + 1
if 6 < hChild.HeadingLevel {
hChild.HeadingLevel = 6
}
}
}
}
mdBuf := &bytes.Buffer{}
for _, hChild := range hChildren {
md, _ = lute.FormatNodeSync(hChild, luteEngine.ParseOptions, luteEngine.RenderOptions)
mdBuf.WriteString(md)
mdBuf.WriteString("\n\n")
}
md = mdBuf.String()
} else {
node := treenode.GetNodeInTree(subTree, sqlBlock.ID)
md, _ = lute.FormatNodeSync(node, luteEngine.ParseOptions, luteEngine.RenderOptions)
}
buf := &bytes.Buffer{}
lines := strings.Split(md, "\n")
for i, line := range lines {
if 0 == blockEmbedMode { // 使用原始文本
buf.WriteString(line)
} else { // 使用引述块
buf.WriteString("> " + line)
}
if i < len(lines)-1 {
buf.WriteString("\n")
}
mdBuf := &bytes.Buffer{}
for _, hChild := range hChildren {
md, _ = lute.FormatNodeSync(hChild, luteEngine.ParseOptions, luteEngine.RenderOptions)
mdBuf.WriteString(md)
mdBuf.WriteString("\n\n")
}
buf.WriteString("\n\n")
md = mdBuf.String()
} else {
node := treenode.GetNodeInTree(subTree, sqlBlock.ID)
md, _ = lute.FormatNodeSync(node, luteEngine.ParseOptions, luteEngine.RenderOptions)
}
subTree = parse.Parse("", buf.Bytes(), luteEngine.ParseOptions)
var inserts []*ast.Node
for subNode := subTree.Root.FirstChild; nil != subNode; subNode = subNode.Next {
if ast.NodeKramdownBlockIAL != subNode.Type {
inserts = append(inserts, subNode)
}
buf := &bytes.Buffer{}
lines := strings.Split(md, "\n")
for i, line := range lines {
if 0 == blockEmbedMode { // 使用原始文本
buf.WriteString(line)
} else { // 使用引述块
buf.WriteString("> " + line)
}
if 2 < len(n.KramdownIAL) && 0 < len(inserts) {
if bookmark := n.IALAttr("bookmark"); "" != bookmark {
inserts[0].SetIALAttr("bookmark", bookmark)
}
if name := n.IALAttr("name"); "" != name {
inserts[0].SetIALAttr("name", name)
}
if alias := n.IALAttr("alias"); "" != alias {
inserts[0].SetIALAttr("alias", alias)
}
if memo := n.IALAttr("memo"); "" != memo {
inserts[0].SetIALAttr("memo", memo)
}
}
for _, insert := range inserts {
n.InsertBefore(insert)
if gulu.Str.Contains(sqlBlock.ID, *resolved) {
return ast.WalkContinue
}
resolveEmbedR(insert, blockEmbedMode, luteEngine, resolved, depth)
if i < len(lines)-1 {
buf.WriteString("\n")
}
}
unlinks = append(unlinks, n)
return ast.WalkSkipChildren
buf.WriteString("\n\n")
subTree = parse.Parse("", buf.Bytes(), luteEngine.ParseOptions)
var inserts []*ast.Node
for subNode := subTree.Root.FirstChild; nil != subNode; subNode = subNode.Next {
if ast.NodeKramdownBlockIAL != subNode.Type {
inserts = append(inserts, subNode)
}
}
if 2 < len(n.KramdownIAL) && 0 < len(inserts) {
if bookmark := n.IALAttr("bookmark"); "" != bookmark {
inserts[0].SetIALAttr("bookmark", bookmark)
}
if name := n.IALAttr("name"); "" != name {
inserts[0].SetIALAttr("name", name)
}
if alias := n.IALAttr("alias"); "" != alias {
inserts[0].SetIALAttr("alias", alias)
}
if memo := n.IALAttr("memo"); "" != memo {
inserts[0].SetIALAttr("memo", memo)
}
}
for _, insert := range inserts {
n.InsertBefore(insert)
if gulu.Str.Contains(sqlBlock.ID, *resolved) {
return ast.WalkContinue
}
resolveEmbedR(insert, blockEmbedMode, luteEngine, resolved, depth)
*depth--
}
}
return ast.WalkContinue
unlinks = append(unlinks, n)
return ast.WalkSkipChildren
})
for _, unlink := range unlinks {
unlink.Unlink()
}

View file

@ -1635,7 +1635,7 @@ func processSyncMergeResult(exit, byHand bool, mergeResult *dejavu.MergeResult,
}
}
removeWidgetDirSet, removePluginSet := hashset.New(), hashset.New()
removeWidgetDirSet, unloadPluginSet, uninstallPluginSet := hashset.New(), hashset.New(), hashset.New()
for _, file := range mergeResult.Removes {
removes = append(removes, file.Path)
if strings.HasPrefix(file.Path, "/storage/riff/") {
@ -1664,7 +1664,8 @@ func processSyncMergeResult(exit, byHand bool, mergeResult *dejavu.MergeResult,
if strings.HasPrefix(file.Path, "/plugins/") {
if parts := strings.Split(file.Path, "/"); 2 < len(parts) {
needReloadPlugin = true
removePluginSet.Add(parts[2])
// 删除插件目录:卸载
uninstallPluginSet.Add(parts[2])
}
}
@ -1681,7 +1682,8 @@ func processSyncMergeResult(exit, byHand bool, mergeResult *dejavu.MergeResult,
}
for _, removePetal := range mergeResult.RemovePetals {
needReloadPlugin = true
removePluginSet.Add(removePetal)
// Petal 中删除插件:卸载
uninstallPluginSet.Add(removePetal)
}
if needReloadFlashcard {
@ -1693,7 +1695,7 @@ func processSyncMergeResult(exit, byHand bool, mergeResult *dejavu.MergeResult,
}
if needReloadPlugin {
PushReloadPlugin(upsertCodePluginSet, upsertDataPluginSet, removePluginSet, "")
PushReloadPlugin(upsertCodePluginSet, upsertDataPluginSet, unloadPluginSet, uninstallPluginSet, "")
}
for _, widgetDir := range removeWidgetDirSet.Values() {

View file

@ -1433,6 +1433,7 @@ func buildTypeFilter(types map[string]bool) string {
s.VideoBlock = types["videoBlock"]
s.IFrameBlock = types["iframeBlock"]
s.WidgetBlock = types["widgetBlock"]
s.Callout = types["callout"]
} else {
s.Document = Conf.Search.Document
s.Heading = Conf.Search.Heading
@ -1451,6 +1452,7 @@ func buildTypeFilter(types map[string]bool) string {
s.VideoBlock = Conf.Search.VideoBlock
s.IFrameBlock = Conf.Search.IFrameBlock
s.WidgetBlock = Conf.Search.WidgetBlock
s.Callout = Conf.Search.Callout
}
return s.TypeFilter()
}

View file

@ -338,6 +338,7 @@ type CriterionTypes struct {
VideoBlock bool `json:"videoBlock"`
IFrameBlock bool `json:"iframeBlock"`
WidgetBlock bool `json:"widgetBlock"`
Callout bool `json:"callout"`
}
type CriterionReplaceTypes struct {

View file

@ -373,7 +373,8 @@ func RenderTemplate(p, id string, preview bool) (tree *parse.Tree, dom string, e
if (ast.NodeListItem == n.Type && (nil == n.FirstChild ||
(3 == n.ListData.Typ && (nil == n.FirstChild.Next || ast.NodeKramdownBlockIAL == n.FirstChild.Next.Type)))) ||
(ast.NodeBlockquote == n.Type && nil != n.FirstChild && nil != n.FirstChild.Next && ast.NodeKramdownBlockIAL == n.FirstChild.Next.Type) {
(ast.NodeBlockquote == n.Type && nil != n.FirstChild && nil != n.FirstChild.Next && ast.NodeKramdownBlockIAL == n.FirstChild.Next.Type) ||
(ast.NodeCallout == n.Type && nil != n.FirstChild && ast.NodeKramdownBlockIAL == n.FirstChild.Next.Type) {
nodesNeedAppendChild = append(nodesNeedAppendChild, n)
}

View file

@ -1612,6 +1612,16 @@ func (tx *Transaction) doUpdate(operation *Operation) (ret *TxErr) {
util.PushEvent(evt)
}
if avNames := getAvNames(updatedNode.IALAttr(av.NodeAttrNameAvs)); "" != avNames {
// updateBlock 会清空数据库角标 https://github.com/siyuan-note/siyuan/issues/16549
go func() {
time.Sleep(200 * time.Millisecond)
oldAttrs := parse.IAL2Map(updatedNode.KramdownIAL)
updatedNode.SetIALAttr(av.NodeAttrViewNames, avNames)
pushBroadcastAttrTransactions(oldAttrs, updatedNode)
}()
}
createdUpdated(updatedNode)
tx.nodes[updatedNode.ID] = updatedNode
if err = tx.writeTree(tree); err != nil {

View file

@ -18,6 +18,7 @@ package server
import (
"bytes"
"errors"
"fmt"
"html/template"
"mime"
@ -217,7 +218,15 @@ func Serve(fastMode bool) {
// 反代服务器启动失败不影响核心服务器启动
}()
if err = http.Serve(ln, ginServer.Handler()); err != nil {
util.HttpServer = &http.Server{
Handler: ginServer,
}
if err = util.HttpServer.Serve(ln); err != nil {
if errors.Is(err, http.ErrServerClosed) {
return
}
if !fastMode {
logging.LogErrorf("boot kernel failed: %s", err)
os.Exit(logging.ExitCodeUnavailablePort)
@ -562,6 +571,7 @@ func serveDebug(ginServer *gin.Engine) {
}
func serveWebSocket(ginServer *gin.Engine) {
util.WebSocketServer = melody.New()
util.WebSocketServer.Config.MaxMessageSize = 1024 * 1024 * 8
ginServer.GET("/ws", func(c *gin.Context) {

View file

@ -565,6 +565,10 @@ func GetFurtherCollections(attrView *av.AttributeView, cachedAttrViews map[strin
continue
}
if nil == kv.Key.Rollup {
continue
}
relKey, _ := attrView.GetKey(kv.Key.Rollup.RelationKeyID)
if nil == relKey {
continue

View file

@ -1358,6 +1358,8 @@ func commitTx(tx *sql.Tx) (err error) {
if err = tx.Commit(); err != nil {
logging.LogErrorf("commit tx failed: %s\n %s", err, logging.ShortStack())
}
closeTxPreparedStmts(tx)
return
}
@ -1380,6 +1382,8 @@ func commitHistoryTx(tx *sql.Tx) (err error) {
if err = tx.Commit(); err != nil {
logging.LogErrorf("commit tx failed: %s\n %s", err, logging.ShortStack())
}
closeTxPreparedStmts(tx)
return
}
@ -1402,16 +1406,80 @@ func commitAssetContentTx(tx *sql.Tx) (err error) {
if err = tx.Commit(); err != nil {
logging.LogErrorf("commit tx failed: %s\n %s", err, logging.ShortStack())
}
closeTxPreparedStmts(tx)
return
}
func prepareExecInsertTx(tx *sql.Tx, stmtSQL string, args []interface{}) (err error) {
stmt, err := tx.Prepare(stmtSQL)
if err != nil {
func closeTxPreparedStmts(tx *sql.Tx) {
if tx == nil {
return
}
cacheKey := txCacheKey(tx)
txStmtCacheLock.Lock()
stmtMap, ok := txStmtCache[cacheKey]
if ok {
delete(txStmtCache, cacheKey)
}
txStmtCacheLock.Unlock()
if !ok {
return
}
for _, stmt := range stmtMap {
_ = stmt.Close()
}
}
var (
txStmtCache = make(map[string]map[string]*sql.Stmt)
txStmtCacheLock = sync.Mutex{}
)
func txCacheKey(tx *sql.Tx) string {
return fmt.Sprintf("%p", tx)
}
func prepareExecInsertTx(tx *sql.Tx, stmtSQL string, args []interface{}) (err error) {
if tx == nil {
return fmt.Errorf("tx is nil")
}
cacheKey := txCacheKey(tx)
txStmtCacheLock.Lock()
stmtMap, ok := txStmtCache[cacheKey]
if !ok {
stmtMap = make(map[string]*sql.Stmt)
txStmtCache[cacheKey] = stmtMap
}
stmt, ok := stmtMap[stmtSQL]
txStmtCacheLock.Unlock()
if !ok {
stmt, err = tx.Prepare(stmtSQL)
if err != nil {
return
}
txStmtCacheLock.Lock()
if existing, exists := txStmtCache[cacheKey][stmtSQL]; exists {
stmt.Close()
stmt = existing
} else {
txStmtCache[cacheKey][stmtSQL] = stmt
}
txStmtCacheLock.Unlock()
}
if _, err = stmt.Exec(args...); err != nil {
logging.LogErrorf("exec database stmt [%s] failed: %s", stmtSQL, err)
if strings.Contains(err.Error(), "database disk image is malformed") {
tx.Rollback()
closeDatabase()
removeDatabaseFile()
logging.LogFatalf(logging.ExitCodeReadOnlyDatabase, "database disk image [%s] is malformed, please restart SiYuan kernel to rebuild it", util.DBPath)
}
logging.LogErrorf("exec database stmt [%s] failed: %s\n %s", stmtSQL, err, logging.ShortStack())
return
}
return
@ -1433,7 +1501,6 @@ func execStmtTx(tx *sql.Tx, stmt string, args ...interface{}) (err error) {
func nSort(n *ast.Node) int {
switch n.Type {
// 以下为块级元素
case ast.NodeHeading:
return 5
case ast.NodeParagraph:
@ -1452,6 +1519,8 @@ func nSort(n *ast.Node) int {
return 20
case ast.NodeBlockquote:
return 20
case ast.NodeCallout:
return 20
case ast.NodeSuperBlock:
return 30
case ast.NodeAttributeView:

View file

@ -224,7 +224,7 @@ func FirstLeafBlock(node *ast.Node) (ret *ast.Node) {
func CountBlockNodes(node *ast.Node) (ret int) {
ast.Walk(node, func(n *ast.Node, entering bool) ast.WalkStatus {
if !entering || !n.IsBlock() || ast.NodeList == n.Type || ast.NodeBlockquote == n.Type || ast.NodeSuperBlock == n.Type {
if !entering || !n.IsBlock() || ast.NodeList == n.Type || ast.NodeBlockquote == n.Type || ast.NodeSuperBlock == n.Type || ast.NodeCallout == n.Type {
return ast.WalkContinue
}
@ -403,6 +403,7 @@ var typeAbbrMap = map[string]string{
"NodeThematicBreak": "tb",
"NodeVideo": "video",
"NodeAudio": "audio",
"NodeCallout": "callout",
// 行级元素
"NodeText": "text",
"NodeImage": "img",
@ -458,6 +459,8 @@ func SubTypeAbbr(n *ast.Node) string {
if 6 == n.HeadingLevel {
return "h6"
}
case ast.NodeCallout:
return n.CalloutType
}
return ""
}

View file

@ -22,6 +22,7 @@ import (
"io/fs"
"path/filepath"
"sort"
"strconv"
"strings"
"github.com/88250/gulu"
@ -29,6 +30,7 @@ import (
"github.com/88250/lute/ast"
"github.com/88250/lute/parse"
"github.com/siyuan-note/filelock"
"github.com/siyuan-note/logging"
"github.com/siyuan-note/siyuan/kernel/util"
)
@ -71,7 +73,7 @@ func NewTree(boxID, p, hp, title string) *parse.Tree {
root.SetIALAttr("id", id)
root.SetIALAttr("updated", util.TimeFromID(id))
ret := &parse.Tree{Root: root, ID: id, Box: boxID, Path: p, HPath: hp}
ret.Root.Spec = "1"
ret.Root.Spec = CurrentSpec
newPara := &ast.Node{Type: ast.NodeParagraph, ID: ast.NewNodeID(), Box: boxID, Path: p}
newPara.SetIALAttr("id", newPara.ID)
newPara.SetIALAttr("updated", util.TimeFromID(newPara.ID))
@ -129,3 +131,62 @@ func NewSpanAnchor(id string) (ret *ast.Node) {
func ContainOnlyDefaultIAL(tree *parse.Tree) bool {
return 5 > len(tree.Root.KramdownIAL)
}
var CurrentSpec = "2"
var ErrSpecTooNew = fmt.Errorf("the document spec is too new")
func CheckSpec(tree *parse.Tree) (err error) {
if CurrentSpec == tree.Root.Spec || "" == tree.Root.Spec {
return
}
spec, err := strconv.Atoi(tree.Root.Spec)
if nil != err {
logging.LogErrorf("parse spec [%s] failed: %s", tree.Root.Spec, err)
return
}
currentSpec, _ := strconv.Atoi(CurrentSpec)
if spec > currentSpec {
logging.LogErrorf("tree spec [%s] is newer than current spec [%s]", tree.Root.Spec, CurrentSpec)
return ErrSpecTooNew
}
return
}
func UpgradeSpec(tree *parse.Tree) (upgraded bool) {
if CurrentSpec == tree.Root.Spec {
return
}
upgradeSpec1(tree)
upgradeSpec2(tree)
return true
}
func upgradeSpec2(tree *parse.Tree) {
oldSpec, err := strconv.Atoi(tree.Root.Spec)
if nil != err {
logging.LogErrorf("parse spec [%s] failed: %s", tree.Root.Spec, err)
return
}
if 2 <= oldSpec {
return
}
// 增加了 Callout
tree.Root.Spec = "2"
}
func upgradeSpec1(tree *parse.Tree) {
if "" != tree.Root.Spec {
return
}
parse.NestedInlines2FlattedSpans(tree, false)
tree.Root.Spec = "1"
return
}

View file

@ -211,6 +211,71 @@ func IsValidPandocBin(binPath string) bool {
return false
}
// 解析符号链接
if real, err := filepath.EvalSymlinks(binPath); err == nil {
binPath = real
}
// 文件信息检查
fi, err := os.Stat(binPath)
if err != nil || fi.IsDir() || !fi.Mode().IsRegular() {
return false
}
// 在 Unix 上要求拥有可执行权限
if !gulu.OS.IsWindows() {
if fi.Mode().Perm()&0111 == 0 {
return false
}
}
// 读取文件头判断是否为二进制并排除脚本(#!
f, err := os.Open(binPath)
if err != nil {
return false
}
defer f.Close()
header := make([]byte, 16)
n, _ := f.Read(header)
header = header[:n]
// 拒绝以 shebang 开头的脚本
if bytes.HasPrefix(header, []byte("#!")) {
return false
}
isBin := false
// 常见二进制魔数ELF, PE("MZ"), Mach-O/FAT
if len(header) >= 4 {
switch {
case bytes.Equal(header[:4], []byte{0x7f, 'E', 'L', 'F'}):
isBin = true // ELF
case bytes.Equal(header[:4], []byte{0xfe, 0xed, 0xfa, 0xce}):
isBin = true // Mach-O
case bytes.Equal(header[:4], []byte{0xce, 0xfa, 0xed, 0xfe}):
isBin = true // Mach-O (swapped)
case bytes.Equal(header[:4], []byte{0xca, 0xfe, 0xba, 0xbe}):
isBin = true // FAT
}
}
// PE only needs first 2 bytes "MZ"
if !isBin && len(header) >= 2 && bytes.Equal(header[:2], []byte{'M', 'Z'}) {
isBin = true
}
// Windows 上允许 .exe 文件(作为补充判断)
if !isBin && gulu.OS.IsWindows() {
ext := strings.ToLower(filepath.Ext(binPath))
if ext == ".exe" {
isBin = true
}
}
if !isBin {
return false
}
cmd := exec.Command(binPath, "--version")
gulu.CmdAttr(cmd)
data, err := cmd.CombinedOutput()

View file

@ -26,7 +26,7 @@ import (
)
var (
WebSocketServer = melody.New()
WebSocketServer *melody.Melody
// map[string]map[string]*melody.Session{}
sessions = sync.Map{} // {appId, {sessionId, session}}

View file

@ -22,6 +22,7 @@ import (
"fmt"
"math/rand"
"mime"
"net/http"
"net/url"
"os"
"path/filepath"
@ -72,6 +73,7 @@ func initEnvVars() {
var (
bootProgress = atomic.Int32{} // 启动进度,从 0 到 100
bootDetails string // 启动细节描述
HttpServer *http.Server // HTTP 伺服器实例
HttpServing = false // 是否 HTTP 伺服已经可用
)
@ -101,7 +103,7 @@ func Boot() {
readOnly := flag.String("readonly", "false", "read-only mode")
accessAuthCode := flag.String("accessAuthCode", "", "access auth code")
ssl := flag.Bool("ssl", false, "for https and wss")
lang := flag.String("lang", "", "ar_SA/de_DE/en_US/es_ES/fr_FR/he_IL/it_IT/ja_JP/pl_PL/pt_BR/ru_RU/tr_TR/zh_CHT/zh_CN")
lang := flag.String("lang", "", "ar_SA/de_DE/en_US/es_ES/fr_FR/he_IL/it_IT/ja_JP/ko_KR/pl_PL/pt_BR/ru_RU/tr_TR/zh_CHT/zh_CN")
mode := flag.String("mode", "prod", "dev/prod")
flag.Parse()
@ -140,7 +142,7 @@ func Boot() {
// The access authorization code command line parameter must be set when deploying via Docker https://github.com/siyuan-note/siyuan/issues/9328
fmt.Printf("the access authorization code command line parameter (--accessAuthCode) must be set when deploying via Docker\n")
fmt.Printf("or you can set the SIYUAN_ACCESS_AUTH_CODE env var")
os.Exit(1)
os.Exit(logging.ExitCodeSecurityRisk)
}
}
}
@ -345,10 +347,19 @@ func ReadWorkspacePaths() (ret []string, err error) {
}
var tmp []string
workspaceBaseDir := filepath.Dir(HomeDir)
for _, d := range ret {
if ContainerIOS == Container && strings.Contains(d, "/Documents/") {
// iOS 端沙箱路径会变化,需要转换为相对路径再拼接当前沙箱中的工作空间基路径
d = d[strings.Index(d, "/Documents/")+len("/Documents/"):]
d = filepath.Join(workspaceBaseDir, d)
}
d = strings.TrimRight(d, " \t\n") // 去掉工作空间路径尾部空格 https://github.com/siyuan-note/siyuan/issues/6353
if gulu.File.IsDir(d) {
tmp = append(tmp, d)
} else {
logging.LogWarnf("workspace path [%s] is not a dir", d)
}
}
ret = tmp

View file

@ -117,6 +117,7 @@ func initWorkspaceDirMobile(workspaceBaseDir string) {
var workspacePaths []string
if !gulu.File.IsExist(workspaceConf) {
logging.LogInfof("workspace conf [%s] not exist, use the default workspace [%s]", workspaceConf, defaultWorkspaceDir)
WorkspaceDir = defaultWorkspaceDir
if !gulu.File.IsDir(WorkspaceDir) {
logging.LogWarnf("use the default workspace [%s] since the specified workspace [%s] is not a dir", WorkspaceDir, defaultWorkspaceDir)
@ -125,6 +126,7 @@ func initWorkspaceDirMobile(workspaceBaseDir string) {
workspacePaths = append(workspacePaths, WorkspaceDir)
} else {
workspacePaths, _ = ReadWorkspacePaths()
if 0 < len(workspacePaths) {
WorkspaceDir = workspacePaths[len(workspacePaths)-1]
if !gulu.File.IsDir(WorkspaceDir) {