108 lines
2.7 KiB
Go
108 lines
2.7 KiB
Go
package document
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"apigo.cc/go/cast"
|
|
"apigo.cc/go/file"
|
|
)
|
|
|
|
// Node 代表文档中的一个节点,可以是一个场景、一个角色或一个知识点。
|
|
type Node struct {
|
|
ID string `json:"id"`
|
|
Title string `json:"title"`
|
|
Content string `json:"content"`
|
|
Type string `json:"type,omitempty"`
|
|
Meta map[string]any `json:"meta,omitempty"`
|
|
Links []string `json:"links,omitempty"` // 与其他节点的关联 (ID 列表)
|
|
Parents []string `json:"parents,omitempty"` // 父节点 (用于层级结构)
|
|
}
|
|
|
|
// Graph 是一种具有关联关系的文档,适用于小说大纲、策划分镜、思维导图等场景。
|
|
type Graph struct {
|
|
filename string
|
|
Title string `json:"title"`
|
|
Nodes map[string]*Node `json:"nodes"`
|
|
}
|
|
|
|
// NewGraph 创建一个新的关系型文档。
|
|
func NewGraph() *Graph {
|
|
return &Graph{
|
|
Nodes: make(map[string]*Node),
|
|
}
|
|
}
|
|
|
|
// AddNode 添加或更新一个节点。
|
|
func (g *Graph) AddNode(n *Node) {
|
|
if n.ID == "" {
|
|
n.ID = cast.To[string](len(g.Nodes) + 1)
|
|
}
|
|
g.Nodes[n.ID] = n
|
|
}
|
|
|
|
// ToJSON 返回文档的结构化 JSON 表示。
|
|
func (g *Graph) ToJSON() string {
|
|
res, _ := cast.ToJSON(g)
|
|
return res
|
|
}
|
|
|
|
// ToMarkdown 将关系型文档转换为带有 Mermaid 图表的 Markdown。
|
|
func (g *Graph) ToMarkdown() string {
|
|
var sb strings.Builder
|
|
sb.WriteString("# " + g.Title + "\n\n")
|
|
|
|
// 生成 Mermaid 关系图
|
|
sb.WriteString("```mermaid\ngraph TD\n")
|
|
for id, node := range g.Nodes {
|
|
label := node.Title
|
|
if label == "" {
|
|
label = id
|
|
}
|
|
// 节点样式根据类型变化
|
|
sb.WriteString(fmt.Sprintf(" %s[\"%s\"]\n", id, label))
|
|
for _, link := range node.Links {
|
|
sb.WriteString(fmt.Sprintf(" %s --> %s\n", id, link))
|
|
}
|
|
for _, parent := range node.Parents {
|
|
sb.WriteString(fmt.Sprintf(" %s --- %s\n", parent, id))
|
|
}
|
|
}
|
|
sb.WriteString("```\n\n")
|
|
|
|
// 生成详细内容
|
|
for _, node := range g.Nodes {
|
|
sb.WriteString("## " + node.Title + " (" + node.ID + ")\n")
|
|
if node.Type != "" {
|
|
sb.WriteString("> Type: " + node.Type + "\n\n")
|
|
}
|
|
sb.WriteString(node.Content + "\n\n")
|
|
}
|
|
|
|
return sb.String()
|
|
}
|
|
|
|
// Save 将文档保存为 JSON 文件。
|
|
func (g *Graph) Save(filename ...string) error {
|
|
path := g.filename
|
|
if len(filename) > 0 && filename[0] != "" {
|
|
path = filename[0]
|
|
}
|
|
if path == "" {
|
|
return fmt.Errorf("no filename specified")
|
|
}
|
|
return file.Write(path, g.ToJSON())
|
|
}
|
|
|
|
// OpenGraph 从 JSON 文件加载关系型文档。如果文件不存在,则返回一个新的空白文档。
|
|
func OpenGraph(filename string) (*Graph, error) {
|
|
g := NewGraph()
|
|
g.filename = filename
|
|
if file.Exists(filename) {
|
|
if err := file.UnmarshalFile(filename, g); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return g, nil
|
|
}
|