Signed-off-by: Daniel <845765@qq.com>
This commit is contained in:
Daniel 2025-09-23 23:22:18 +08:00
parent 58f72678ae
commit be233229a9
No known key found for this signature in database
GPG key ID: 86211BA83DF03017
4 changed files with 73 additions and 29 deletions

View file

@ -6,7 +6,7 @@ import {blockRender} from "../render/blockRender";
import {processRender} from "../util/processCode"; import {processRender} from "../util/processCode";
import {highlightRender} from "../render/highlightRender"; import {highlightRender} from "../render/highlightRender";
import {hasClosestBlock, hasClosestByAttribute, hasTopClosestByAttribute, isInEmbedBlock} from "../util/hasClosest"; import {hasClosestBlock, hasClosestByAttribute, hasTopClosestByAttribute, isInEmbedBlock} from "../util/hasClosest";
import {setFold, zoomOut} from "../../menus/protyle"; import {zoomOut} from "../../menus/protyle";
import {disabledProtyle, enableProtyle, onGet} from "../util/onGet"; import {disabledProtyle, enableProtyle, onGet} from "../util/onGet";
/// #if !MOBILE /// #if !MOBILE
import {getAllModels} from "../../layout/getAll"; import {getAllModels} from "../../layout/getAll";
@ -1061,10 +1061,6 @@ export const turnsIntoTransaction = (options: {
const undoOperations: IOperation[] = []; const undoOperations: IOperation[] = [];
let previousId: string; let previousId: string;
selectsElement.forEach((item, index) => { selectsElement.forEach((item, index) => {
if ((options.type === "Blocks2Ps" || options.type === "Blocks2Hs") &&
item.getAttribute("data-type") === "NodeHeading" && item.getAttribute("fold") === "1") {
setFold(options.protyle, item, undefined, undefined, false);
}
item.classList.remove("protyle-wysiwyg--select"); item.classList.remove("protyle-wysiwyg--select");
item.removeAttribute("select-start"); item.removeAttribute("select-start");
item.removeAttribute("select-end"); item.removeAttribute("select-end");

View file

@ -88,6 +88,30 @@ func (tx *Transaction) doUnfoldHeading(operation *Operation) (ret *TxErr) {
return &TxErr{code: TxErrCodeBlockNotFound, id: headingID} return &TxErr{code: TxErrCodeBlockNotFound, id: headingID}
} }
luteEngine := NewLute()
parentFoldedHeading := treenode.GetParentFoldedHeading(heading)
if nil != parentFoldedHeading {
// 如果当前标题在上方某个折叠的标题下方,则展开上方那个折叠标题以保持一致性
children := treenode.HeadingChildren(parentFoldedHeading)
for _, child := range children {
ast.Walk(child, func(n *ast.Node, entering bool) ast.WalkStatus {
if !entering || !n.IsBlock() {
return ast.WalkContinue
}
n.RemoveIALAttr("heading-fold")
n.RemoveIALAttr("fold")
return ast.WalkContinue
})
}
parentFoldedHeading.RemoveIALAttr("fold")
parentFoldedHeading.RemoveIALAttr("heading-fold")
go func() {
tx.WaitForCommit()
ReloadProtyle(tree.ID)
}()
}
children := treenode.HeadingChildren(heading) children := treenode.HeadingChildren(heading)
for _, child := range children { for _, child := range children {
ast.Walk(child, func(n *ast.Node, entering bool) ast.WalkStatus { ast.Walk(child, func(n *ast.Node, entering bool) ast.WalkStatus {
@ -116,7 +140,6 @@ func (tx *Transaction) doUnfoldHeading(operation *Operation) (ret *TxErr) {
// 展开折叠的标题后显示块引用计数 Display reference counts after unfolding headings https://github.com/siyuan-note/siyuan/issues/13618 // 展开折叠的标题后显示块引用计数 Display reference counts after unfolding headings https://github.com/siyuan-note/siyuan/issues/13618
fillBlockRefCount(children) fillBlockRefCount(children)
luteEngine := NewLute()
operation.RetData = renderBlockDOMByNodes(children, luteEngine) operation.RetData = renderBlockDOMByNodes(children, luteEngine)
return return
} }

View file

@ -1255,10 +1255,16 @@ func (tx *Transaction) doInsert(operation *Operation) (ret *TxErr) {
} }
node.InsertAfter(insertedNode) node.InsertAfter(insertedNode)
if treenode.IsUnderFoldedHeading(insertedNode) { if parentFoldedHeading := treenode.GetParentFoldedHeading(insertedNode); nil != parentFoldedHeading {
// 保持在标题下的折叠状态 ast.Walk(insertedNode, func(n *ast.Node, entering bool) ast.WalkStatus {
insertedNode.SetIALAttr("fold", "1") if !entering || !n.IsBlock() {
insertedNode.SetIALAttr("heading-fold", "1") return ast.WalkContinue
}
n.SetIALAttr("fold", "1")
n.SetIALAttr("heading-fold", "1")
return ast.WalkContinue
})
} }
} else { } else {
node = treenode.GetNodeInTree(tree, operation.ParentID) node = treenode.GetNodeInTree(tree, operation.ParentID)
@ -1510,14 +1516,23 @@ func (tx *Transaction) doUpdate(operation *Operation) (ret *TxErr) {
oldNode.InsertAfter(updatedNode) oldNode.InsertAfter(updatedNode)
oldNode.Unlink() oldNode.Unlink()
if treenode.IsUnderFoldedHeading(updatedNode) { parentFoldedHeading := treenode.GetParentFoldedHeading(updatedNode)
// 保持在标题下的折叠状态 if nil != parentFoldedHeading {
updatedNode.SetIALAttr("fold", "1") children := treenode.HeadingChildren(parentFoldedHeading)
updatedNode.SetIALAttr("heading-fold", "1") for _, child := range children {
ast.Walk(child, func(n *ast.Node, entering bool) ast.WalkStatus {
if !entering || !n.IsBlock() {
return ast.WalkContinue
}
n.SetIALAttr("fold", "1")
n.SetIALAttr("heading-fold", "1")
return ast.WalkContinue
})
}
} }
createdUpdated(updatedNode) createdUpdated(updatedNode)
tx.nodes[updatedNode.ID] = updatedNode tx.nodes[updatedNode.ID] = updatedNode
if err = tx.writeTree(tree); err != nil { if err = tx.writeTree(tree); err != nil {
return &TxErr{code: TxErrCodeWriteTree, msg: err.Error(), id: id} return &TxErr{code: TxErrCodeWriteTree, msg: err.Error(), id: id}
@ -1544,6 +1559,14 @@ func (tx *Transaction) doUpdate(operation *Operation) (ret *TxErr) {
} }
} }
} }
if oldNode.HeadingLevel != updatedNode.HeadingLevel {
// 编辑折叠标题下方块,并且这个块的标题层级发生了变化(比如从 H2 变为 H3 或者从标题块变成段落块),则刷新所有编辑器以保持一致性
go func() {
tx.WaitForCommit()
ReloadProtyle(tree.ID)
}()
}
return return
} }

View file

@ -85,29 +85,31 @@ func GetHeadingFold(nodes []*ast.Node) (ret []*ast.Node) {
return return
} }
func IsUnderFoldedHeading(node *ast.Node) bool { func GetParentFoldedHeading(node *ast.Node) (parentFoldedHeading *ast.Node) {
currentLevel := 7 currentLevel := 7
if ast.NodeHeading == node.Type { if ast.NodeHeading == node.Type {
currentLevel = node.HeadingLevel currentLevel = node.HeadingLevel
} }
for n := node.Previous; nil != n; n = n.Previous { for n := node.Previous; nil != n; n = n.Previous {
if ast.NodeHeading == n.Type { if ast.NodeHeading != n.Type {
if n.HeadingLevel >= currentLevel { continue
break }
}
currentLevel = n.HeadingLevel
if "1" == n.IALAttr("fold") { if n.HeadingLevel >= currentLevel {
if ast.NodeHeading != node.Type { break
return true }
} currentLevel = n.HeadingLevel
if n.HeadingLevel > node.HeadingLevel {
return true if "1" == n.IALAttr("fold") {
} if ast.NodeHeading != node.Type {
parentFoldedHeading = n
}
if n.HeadingLevel < node.HeadingLevel {
parentFoldedHeading = n
} }
} }
} }
return false return
} }
func HeadingChildren(heading *ast.Node) (ret []*ast.Node) { func HeadingChildren(heading *ast.Node) (ret []*ast.Node) {