diff --git a/README_zh_CN.md b/README_zh_CN.md
index 7d712fb37..c20fcdc74 100644
--- a/README_zh_CN.md
+++ b/README_zh_CN.md
@@ -42,6 +42,7 @@
* [安装包](#安装包)
* [Docker 部署](#docker-部署)
* [Unraid 部署](#unraid-部署)
+ * [宝塔面板 部署](#宝塔面板部署)
* [内部预览版](#内部预览版)
* [🏘️ 社区](#️-社区)
* [🛠️ 开发指南](#️-开发指南)
@@ -300,6 +301,38 @@ Publish parameters: --accessAuthCode=******(访问授权码)
+### 宝塔面板部署
+
+
+宝塔面板 部署文档
+
+#### 前提
+
+* 仅适用于宝塔面板9.2.0及以上版本
+* 安装宝塔面板,前往[宝塔面板](https://www.bt.cn/new/download.html)官网,选择正式版的脚本下载安装
+
+#### 部署
+
+1. 登录宝塔面板,在左侧菜单栏中点击 `Docker`
+2. 首次会提示安装 `Docker` 和 `Docker Compose` 服务,点击立即安装,若已安装请忽略
+3. 安装完成后在 `Docker-应用商店-实用工具` 中找到 `思源笔记`,点击`安装`,也可以在搜索框直接搜索
+4. 设置域名等基本信息,点击 `确定`
+ * 名称:应用名称,默认 `siyuan_随机字符`
+ * 版本选择:默认 `latest`
+ * 域名:如你需要通过域名访问,请在此处填写你的域名
+ * 允许外部访问:如你需通过 `IP+Port` 直接访问,请勾选,如你已经设置了域名,请不要勾选此处
+ * 端口:默认 `6806`,可自行修改
+ * 访问授权码:默认随机生成
+ * 内存限制:0为不限制,根据实际需要设置
+5. 提交后面板会自动进行应用初始化,大概需要`1-3`分钟,初始化完成后即可访问
+
+#### 访问思源笔记
+
+* 如果你填写了域名,请在浏览器输入域名访问
+* 如你选择了 `IP+端口`,请在浏览器地输入 `http://<宝塔面板IP>:6806` 访问
+
+
+
### 内部预览版
我们会在有重大更新前发布内部预览版,请访问 [https://github.com/siyuan-note/insider](https://github.com/siyuan-note/insider)。
diff --git a/app/src/block/popover.ts b/app/src/block/popover.ts
index 37c9abb89..ba917e5e3 100644
--- a/app/src/block/popover.ts
+++ b/app/src/block/popover.ts
@@ -37,6 +37,7 @@ export const initBlockPopover = (app: App) => {
if (aElement.firstElementChild?.getAttribute("data-type") === "url") {
if (aElement.firstElementChild.textContent.indexOf("...") > -1) {
tip = Lute.EscapeHTMLStr(aElement.firstElementChild.getAttribute("data-href"));
+ tooltipClass = "href";
}
}
if (!tip && aElement.dataset.wrap !== "true" && event.target.dataset.type !== "block-more" && !hasClosestByClassName(event.target, "block__icon")) {
@@ -49,6 +50,7 @@ export const initBlockPopover = (app: App) => {
}
} else if (aElement.classList.contains("av__celltext--url")) {
tip = tip ? `${tip.substring(0, Constants.SIZE_TITLE)}
${aElement.getAttribute("data-name")}` : aElement.getAttribute("data-name");
+ tooltipClass = "href";
} else if (aElement.classList.contains("av__calc--ashow") && aElement.clientWidth + 2 < aElement.scrollWidth) {
tip = aElement.lastChild.textContent + " " + aElement.firstElementChild.textContent;
}
@@ -76,7 +78,7 @@ export const initBlockPopover = (app: App) => {
} else {
assetTip += ` ${response.data.hSize}${title ? '' + title : ""}
${window.siyuan.languages.modifiedAt} ${response.data.hUpdated}
${window.siyuan.languages.createdAt} ${response.data.hCreated}`;
}
- showTooltip(assetTip, aElement);
+ showTooltip(assetTip, aElement, tooltipClass);
});
tip = "";
} else if (title) {
diff --git a/kernel/filesys/tree.go b/kernel/filesys/tree.go
index 24aa96953..ccc3d7371 100644
--- a/kernel/filesys/tree.go
+++ b/kernel/filesys/tree.go
@@ -150,29 +150,22 @@ func LoadTreeByData(data []byte, boxID, p string, luteEngine *lute.Lute) (ret *p
parentAbsPath += ".sy"
parentPath := parentAbsPath
parentAbsPath = filepath.Join(util.DataDir, boxID, parentAbsPath)
- parentData, readErr := filelock.ReadFile(parentAbsPath)
- if nil != readErr {
- if os.IsNotExist(readErr) {
- // 子文档缺失父文档时自动补全 https://github.com/siyuan-note/siyuan/issues/7376
- parentTree := treenode.NewTree(boxID, parentPath, hPathBuilder.String()+"Untitled", "Untitled")
- if _, writeErr := WriteTree(parentTree); nil != writeErr {
- logging.LogErrorf("rebuild parent tree [%s] failed: %s", parentAbsPath, writeErr)
- } else {
- logging.LogInfof("rebuilt parent tree [%s]", parentAbsPath)
- treenode.UpsertBlockTree(parentTree)
- }
+
+ parentDocIAL := DocIAL(parentAbsPath)
+ if 1 > len(parentDocIAL) {
+ // 子文档缺失父文档时自动补全 https://github.com/siyuan-note/siyuan/issues/7376
+ parentTree := treenode.NewTree(boxID, parentPath, hPathBuilder.String()+"Untitled", "Untitled")
+ if _, writeErr := WriteTree(parentTree); nil != writeErr {
+ logging.LogErrorf("rebuild parent tree [%s] failed: %s", parentAbsPath, writeErr)
} else {
- logging.LogWarnf("read parent tree data [%s] failed: %s", parentAbsPath, readErr)
+ logging.LogInfof("rebuilt parent tree [%s]", parentAbsPath)
+ treenode.UpsertBlockTree(parentTree)
}
hPathBuilder.WriteString("Untitled/")
continue
}
- ial := ReadDocIAL(parentData)
- if 1 > len(ial) {
- logging.LogWarnf("tree [%s] is corrupted", filepath.Join(boxID, p))
- }
- title := ial["title"]
+ title := parentDocIAL["title"]
if "" == title {
title = "Untitled"
}
@@ -185,6 +178,29 @@ func LoadTreeByData(data []byte, boxID, p string, luteEngine *lute.Lute) (ret *p
return
}
+func DocIAL(absPath string) (ret map[string]string) {
+ filelock.Lock(absPath)
+ file, err := os.Open(absPath)
+ if err != nil {
+ logging.LogErrorf("open file [%s] failed: %s", absPath, err)
+ filelock.Unlock(absPath)
+ return nil
+ }
+
+ iter := jsoniter.Parse(jsoniter.ConfigCompatibleWithStandardLibrary, file, 512)
+ for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
+ if field == "Properties" {
+ iter.ReadVal(&ret)
+ break
+ } else {
+ iter.Skip()
+ }
+ }
+ file.Close()
+ filelock.Unlock(absPath)
+ return
+}
+
func WriteTree(tree *parse.Tree) (size uint64, err error) {
data, filePath, err := prepareWriteTree(tree)
if err != nil {
diff --git a/kernel/model/file.go b/kernel/model/file.go
index d0be5ba06..b65909b71 100644
--- a/kernel/model/file.go
+++ b/kernel/model/file.go
@@ -37,7 +37,6 @@ import (
"github.com/88250/lute/html"
"github.com/88250/lute/parse"
util2 "github.com/88250/lute/util"
- jsoniter "github.com/json-iterator/go"
"github.com/siyuan-note/filelock"
"github.com/siyuan-note/logging"
"github.com/siyuan-note/riff"
@@ -116,29 +115,9 @@ func (box *Box) docIAL(p string) (ret map[string]string) {
}
filePath := filepath.Join(util.DataDir, box.ID, p)
-
- filelock.Lock(filePath)
- file, err := os.Open(filePath)
- if err != nil {
- logging.LogErrorf("open file [%s] failed: %s", p, err)
- filelock.Unlock(filePath)
- return nil
- }
-
- iter := jsoniter.Parse(jsoniter.ConfigCompatibleWithStandardLibrary, file, 512)
- for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
- if field == "Properties" {
- iter.ReadVal(&ret)
- break
- } else {
- iter.Skip()
- }
- }
- file.Close()
- filelock.Unlock(filePath)
-
+ ret = filesys.DocIAL(filePath)
if 1 > len(ret) {
- logging.LogWarnf("properties not found in file [%s]", p)
+ logging.LogWarnf("properties not found in file [%s]", filePath)
box.moveCorruptedData(filePath)
return nil
}
diff --git a/kernel/model/outline.go b/kernel/model/outline.go
index 5e45b1f34..4e819d511 100644
--- a/kernel/model/outline.go
+++ b/kernel/model/outline.go
@@ -17,10 +17,8 @@
package model
import (
- "github.com/88250/lute/html"
- "time"
-
"github.com/88250/lute/ast"
+ "github.com/88250/lute/html"
"github.com/88250/lute/parse"
"github.com/emirpasic/gods/stacks/linkedliststack"
"github.com/siyuan-note/siyuan/kernel/treenode"
@@ -209,7 +207,6 @@ func (tx *Transaction) doMoveOutlineHeading(operation *Operation) (ret *TxErr) {
}
func Outline(rootID string, preview bool) (ret []*Path, err error) {
- time.Sleep(util.FrontendQueueInterval)
FlushTxQueue()
ret = []*Path{}
diff --git a/kernel/util/ocr.go b/kernel/util/ocr.go
index f7d2db15d..8dadbf402 100644
--- a/kernel/util/ocr.go
+++ b/kernel/util/ocr.go
@@ -228,10 +228,11 @@ func Tesseract(imgAbsPath string) (ret []map[string]interface{}) {
}
tsv := string(output)
- logging.LogInfof("tesseract [path=%s, size=%d] success", imgAbsPath, info.Size())
+ //logging.LogInfof("tesseract [path=%s] success [%s]", imgAbsPath, tsv)
// 按行分割 TSV 数据
- lines := strings.Split(tsv, "\r\n")
+ tsv = strings.ReplaceAll(tsv, "\r", "")
+ lines := strings.Split(tsv, "\n")
// 解析 TSV 数据 跳过标题行,从第二行开始处理
for _, line := range lines[1:] {
@@ -262,7 +263,7 @@ func GetOcrJsonText(jsonData []map[string]interface{}) (ret string) {
if text, ok := dataMap["text"]; ok {
// 确保 text 是字符串类型
if textStr, ok := text.(string); ok {
- ret += " " + textStr
+ ret += " " + strings.ReplaceAll(textStr, "\r", "")
}
}
}
diff --git a/kernel/util/runtime.go b/kernel/util/runtime.go
index 30adbea1e..4182d7042 100644
--- a/kernel/util/runtime.go
+++ b/kernel/util/runtime.go
@@ -132,9 +132,6 @@ func SetNetworkProxy(proxyURL string) {
}
const (
- // FrontendQueueInterval 为前端请求队列轮询间隔。
- FrontendQueueInterval = 512 * time.Millisecond
-
// SQLFlushInterval 为数据库事务队列写入间隔。
SQLFlushInterval = 3000 * time.Millisecond
)