diff --git a/core/wiki/chart/block.go b/core/wiki/chart/block.go
new file mode 100644
index 00000000..2f2ce938
--- /dev/null
+++ b/core/wiki/chart/block.go
@@ -0,0 +1,92 @@
+package chart
+
+import (
+ "strings"
+
+ ice "shylinux.com/x/icebergs"
+ "shylinux.com/x/icebergs/core/wiki"
+ kit "shylinux.com/x/toolkits"
+)
+
+type Block struct {
+ Text string
+ FontSize int
+ FontColor string
+ BackGround string
+
+ TextData string
+ RectData string
+
+ Padding int
+ MarginX int
+ MarginY int
+
+ Height int
+ Width int
+ x, y int
+}
+
+func (b *Block) Init(m *ice.Message, arg ...string) wiki.Chart {
+ b.FontSize = kit.Int(kit.Select("24", m.Option(wiki.FONT_SIZE)))
+ b.Padding = kit.Int(kit.Select("10", m.Option(wiki.PADDING)))
+ b.MarginX = kit.Int(kit.Select("10", m.Option(wiki.MARGINX)))
+ b.MarginY = kit.Int(kit.Select("10", m.Option(wiki.MARGINY)))
+
+ if len(arg) > 0 {
+ b.Text = arg[0]
+ }
+ return b
+}
+func (b *Block) Data(m *ice.Message, meta interface{}) wiki.Chart {
+ b.Text = kit.Select(b.Text, kit.Value(meta, kit.MDB_TEXT))
+ kit.Fetch(meta, func(key string, value string) {
+ switch key {
+ case wiki.FG:
+ b.TextData += kit.Format("%s='%s' ", wiki.FILL, value)
+ case wiki.BG:
+ b.RectData += kit.Format("%s='%s' ", wiki.FILL, value)
+ }
+ })
+ kit.Fetch(kit.Value(meta, "data"), func(key string, value string) {
+ b.TextData += kit.Format("%s='%s' ", key, value)
+ })
+ kit.Fetch(kit.Value(meta, "rect"), func(key string, value string) {
+ b.RectData += kit.Format("%s='%s' ", key, value)
+ })
+ return b
+}
+func (b *Block) Draw(m *ice.Message, x, y int) wiki.Chart {
+ float := 3
+ if strings.Contains(m.Option(ice.MSG_USERUA), "iPhone") {
+ float += 0
+ }
+ if m.Option(HIDE_BLOCK) != ice.TRUE {
+ item := wiki.NewItem([]string{`").Dump(m)
+ }
+ item := wiki.NewItem([]string{`%v`, b.Text).Dump(m)
+ return b
+}
+
+func (b *Block) GetHeight(str ...string) int {
+ if b.Height != 0 {
+ return b.Height
+ }
+ return b.FontSize + b.Padding
+}
+func (b *Block) GetWidth(str ...string) int {
+ if b.Width != 0 {
+ return b.Width
+ }
+ s := kit.Select(b.Text, str, 0)
+ cn := (len(s) - len([]rune(s))) / 2
+ en := len([]rune(s)) - cn
+ return cn*b.FontSize + en*b.FontSize*10/16 + b.Padding
+}
+func (b *Block) GetWidths(str ...string) int {
+ return b.GetWidth(str...) + b.MarginX
+}
+func (b *Block) GetHeights(str ...string) int {
+ return b.GetHeight() + b.MarginY
+}
diff --git a/core/wiki/chart/chain.go b/core/wiki/chart/chain.go
new file mode 100644
index 00000000..7fb45c99
--- /dev/null
+++ b/core/wiki/chart/chain.go
@@ -0,0 +1,120 @@
+package chart
+
+import (
+ ice "shylinux.com/x/icebergs"
+ "shylinux.com/x/icebergs/base/cli"
+ "shylinux.com/x/icebergs/base/lex"
+ "shylinux.com/x/icebergs/base/nfs"
+ "shylinux.com/x/icebergs/core/wiki"
+ kit "shylinux.com/x/toolkits"
+)
+
+type Chain struct {
+ data map[string]interface{}
+ Group *wiki.Group
+ Block
+}
+
+func (c *Chain) Init(m *ice.Message, arg ...string) wiki.Chart {
+ (&c.Block).Init(m)
+
+ // 解析数据
+ m.Option(nfs.CAT_CONTENT, arg[0])
+ m.Option(lex.SPLIT_SPACE, "\t \n")
+ m.Option(lex.SPLIT_BLOCK, "\t \n")
+ c.data = lex.Split(m, "", kit.MDB_TEXT)
+
+ // 计算尺寸
+ c.Height = c.size(m, c.data) * c.GetHeights()
+ c.Draw(m, 0, 0)
+ c.Width += 200
+ m.Set(ice.MSG_RESULT)
+ return c
+}
+func (c *Chain) Draw(m *ice.Message, x, y int) wiki.Chart {
+ c.Group = wiki.NewGroup(m, SHIP)
+ defer c.Group.Dump(m, SHIP)
+ c.draw(m, c.data, x, y, &c.Block)
+ return c
+}
+func (c *Chain) size(m *ice.Message, root map[string]interface{}) (height int) {
+ meta := kit.GetMeta(root)
+ if list, ok := root[kit.MDB_LIST].([]interface{}); ok && len(list) > 0 {
+ kit.Fetch(root[kit.MDB_LIST], func(index int, value map[string]interface{}) {
+ height += c.size(m, value)
+ })
+ } else {
+ height = 1
+ }
+ meta[wiki.HEIGHT] = height
+ return height
+}
+func (c *Chain) draw(m *ice.Message, root map[string]interface{}, x, y int, p *Block) int {
+ meta := kit.GetMeta(root)
+ c.Height, c.Width = 0, 0
+
+ if kit.Format(meta[wiki.FG]) != "" {
+ items := wiki.NewItem([]string{"").Dump(m)
+ defer m.Echo("")
+ }
+
+ // 当前节点
+ item := &Block{
+ FontSize: p.FontSize,
+ Padding: p.Padding,
+ MarginX: p.MarginX,
+ MarginY: p.MarginY,
+ }
+ item.x, item.y = x, y+(kit.Int(meta[wiki.HEIGHT])-1)*c.GetHeights()/2
+ item.Init(m, kit.Format(meta[kit.MDB_TEXT])).Data(m, meta)
+ item.Draw(m, item.x, item.y)
+
+ // 画面尺寸
+ if item.y+item.GetHeight()+c.MarginY > c.Height {
+ c.Height = item.y + item.GetHeight() + c.MarginY
+ }
+ if item.x+item.GetWidth()+c.MarginX > c.Width {
+ c.Width = item.x + item.GetWidth() + c.MarginX
+ }
+
+ // 模块连线
+ if p != nil && p.y != 0 {
+ x1, y1 := p.x+p.GetWidths()-(p.MarginX+item.MarginX)/4, p.y+p.GetHeights()/2
+ x4, y4 := item.x+(p.MarginX+item.MarginX)/4, item.y+item.GetHeights()/2
+ c.Group.Echo(SHIP, ``,
+ x1, y1, x1+(x4-x1)/4, y1, x1+(x4-x1)/2, y1+(y4-y1)/2, x4, y4)
+ }
+
+ // 递归节点
+ h, x := 0, x+item.GetWidths()
+ if kit.Fetch(root[kit.MDB_LIST], func(index int, value map[string]interface{}) {
+ h += c.draw(m, value, x, y+h, item)
+ }); h == 0 {
+ return item.GetHeights()
+ }
+ return h
+}
+
+const (
+ SHIP = "ship"
+
+ HIDE_BLOCK = "hide-block"
+)
+const CHAIN = "chain"
+
+func init() {
+ wiki.AddChart(CHAIN, func(m *ice.Message) wiki.Chart {
+ m.Option(wiki.STROKE_WIDTH, "1")
+ m.Option(wiki.FILL, cli.BLUE)
+ m.Option(wiki.MARGINX, "40")
+ m.Option(wiki.MARGINY, "0")
+
+ m.Option(COMPACT, ice.TRUE)
+ m.Option(HIDE_BLOCK, ice.TRUE)
+ wiki.AddGroupOption(m, SHIP, wiki.STROKE_WIDTH, "1", wiki.STROKE, cli.CYAN, wiki.FILL, "none")
+ return &Chain{}
+ })
+}
diff --git a/core/wiki/chart/label.go b/core/wiki/chart/label.go
new file mode 100644
index 00000000..58bf0dae
--- /dev/null
+++ b/core/wiki/chart/label.go
@@ -0,0 +1,87 @@
+package chart
+
+import (
+ "strings"
+
+ ice "shylinux.com/x/icebergs"
+ "shylinux.com/x/icebergs/core/wiki"
+ kit "shylinux.com/x/toolkits"
+)
+
+type Label struct {
+ data [][]string
+ max map[int]int
+ Block
+}
+
+func (l *Label) Init(m *ice.Message, arg ...string) wiki.Chart {
+ (&l.Block).Init(m)
+
+ // 解析数据
+ l.max = map[int]int{}
+ for _, v := range strings.Split(arg[0], ice.NL) {
+ ls := kit.Split(v, ice.SP, ice.SP)
+ l.data = append(l.data, ls)
+
+ for i, v := range ls {
+ switch data := kit.Parse(nil, "", kit.Split(v)...).(type) {
+ case map[string]interface{}:
+ v = kit.Select("", data[kit.MDB_TEXT])
+ }
+ if w := l.GetWidth(v); w > l.max[i] {
+ l.max[i] = w
+ }
+ }
+ }
+
+ // 计算尺寸
+ l.Height = len(l.data) * l.GetHeights()
+ for _, v := range l.max {
+ l.Width += v + l.MarginX
+ }
+ return l
+}
+func (l *Label) Draw(m *ice.Message, x, y int) wiki.Chart {
+ var item *Block
+ top := y
+ for _, line := range l.data {
+ left := x
+ for i, text := range line {
+
+ // 数据
+ item = &Block{FontSize: l.FontSize, Padding: l.Padding, MarginX: l.MarginX, MarginY: l.MarginY}
+ switch data := kit.Parse(nil, "", kit.Split(text)...).(type) {
+ case map[string]interface{}:
+ item.Init(m, kit.Select(text, data[kit.MDB_TEXT])).Data(m, data)
+ default:
+ item.Init(m, text)
+ }
+
+ // 输出
+ switch m.Option(COMPACT) {
+ case "max":
+ item.Width = l.Width/len(line) - l.MarginX
+ case ice.TRUE:
+
+ default:
+ item.Width = l.max[i]
+ }
+ item.Draw(m, left, top)
+
+ left += item.GetWidths()
+ }
+ top += item.GetHeights()
+ }
+ return l
+}
+
+const (
+ COMPACT = "compact"
+)
+const LABEL = "label"
+
+func init() {
+ wiki.AddChart(LABEL, func(m *ice.Message) wiki.Chart {
+ return &Label{}
+ })
+}
diff --git a/core/wiki/chart/sequence.go b/core/wiki/chart/sequence.go
new file mode 100644
index 00000000..cd62fc27
--- /dev/null
+++ b/core/wiki/chart/sequence.go
@@ -0,0 +1,156 @@
+package chart
+
+import (
+ ice "shylinux.com/x/icebergs"
+ "shylinux.com/x/icebergs/base/cli"
+ "shylinux.com/x/icebergs/base/lex"
+ "shylinux.com/x/icebergs/base/nfs"
+ "shylinux.com/x/icebergs/core/wiki"
+ kit "shylinux.com/x/toolkits"
+)
+
+type Sequence struct {
+ Head []string
+ List [][]map[string]interface{}
+ pos []int
+ Block
+}
+
+func (s *Sequence) push(m *ice.Message, list string, arg ...interface{}) map[string]interface{} {
+ node, node_list := kit.Dict(arg...), kit.Int(list)
+ s.List[node_list] = append(s.List[node_list], node)
+ _max := kit.Max(len(s.List[node_list])-1, s.pos[node_list])
+ node[kit.MDB_ORDER], s.pos[node_list] = _max, _max+1
+ return node
+}
+func (s *Sequence) Init(m *ice.Message, arg ...string) wiki.Chart {
+ (&s.Block).Init(m)
+
+ // 解析数据
+ m.Cmd(lex.SPLIT, "", kit.Dict(nfs.CAT_CONTENT, arg[0]), func(ls []string, data map[string]interface{}) []string {
+ if len(s.Head) == 0 {
+ s.Head, s.pos = ls, make([]int, len(ls))
+ for i := 0; i < len(ls); i++ {
+ s.List = append(s.List, []map[string]interface{}{})
+ }
+ return ls
+ }
+
+ from_node := s.push(m, ls[0])
+ list := map[string]map[string]interface{}{ls[0]: from_node}
+ for i := 1; i < len(ls)-1; i += 2 {
+ to_node := list[ls[i+1]]
+ if to_node == nil {
+ to_node = s.push(m, ls[i+1])
+ list[ls[i+1]] = to_node
+
+ _max := kit.Max(kit.Int(from_node[kit.MDB_ORDER]), kit.Int(to_node[kit.MDB_ORDER]))
+ s.pos[kit.Int(ls[i-1])], s.pos[kit.Int(ls[i+1])] = _max+1, _max+1
+ from_node[kit.MDB_ORDER], to_node[kit.MDB_ORDER] = _max, _max
+ from_node[kit.MDB_TEXT], from_node[kit.MDB_NEXT] = ls[i], ls[i+1]
+ } else {
+ from_node[kit.MDB_ECHO], from_node[kit.MDB_PREV] = ls[i], ls[i+1]
+ }
+ from_node = to_node
+ }
+ return ls
+ })
+
+ // 计算尺寸
+ width := 0
+ for _, v := range s.Head {
+ width += s.Block.GetWidths(v)
+ }
+ rect_height := kit.Int(m.Option(RECT + "-" + wiki.HEIGHT))
+ s.Width, s.Height = width, kit.Max(s.pos...)*(rect_height+s.MarginY)+s.MarginY+s.GetHeights()
+ return s
+}
+func (s *Sequence) Draw(m *ice.Message, x, y int) wiki.Chart {
+ g := wiki.NewGroup(m, ARROW, HEAD, LINE, RECT, NEXT, PREV, TEXT, ECHO)
+ arrow_height := kit.Int(g.Option(ARROW, wiki.HEIGHT))
+ arrow_width := kit.Int(g.Option(ARROW, wiki.WIDTH))
+ rect_height := kit.Int(g.Option(RECT, wiki.HEIGHT))
+ rect_width := kit.Int(g.Option(RECT, wiki.WIDTH))
+ g.DefsArrow(NEXT, arrow_height, arrow_width)
+
+ height := s.Height
+ s.Block.Height, s.Block.Width = 0, 0
+ line_pos := make([]int, len(s.List))
+ for i := range s.List {
+ s.Block.Text = s.Head[i]
+ s.Block.Draw(g.Get(HEAD), x, y)
+ line_pos[i], x = x+s.Block.GetWidths()/2, x+s.Block.GetWidths()
+ }
+
+ y += s.Block.GetHeight() + s.MarginY/2
+ for _, x := range line_pos {
+ g.EchoLine(LINE, x, y, x, height-s.MarginY/2)
+ }
+
+ for i, x := range line_pos {
+ for _, v := range s.List[i] {
+ pos := kit.Int(v[kit.MDB_ORDER])
+ g.EchoRect(RECT, rect_height, rect_width, x-rect_width/2, y+pos*(rect_height+s.MarginY)+s.MarginY, "2", "2")
+
+ yy := y + pos*(rect_height+s.MarginY) + s.MarginY + rect_height/4
+ if kit.Format(v[kit.MDB_NEXT]) != "" {
+ xx := line_pos[kit.Int(v[kit.MDB_NEXT])]
+ if x < xx {
+ g.EchoArrowLine(NEXT, x+rect_width/2, yy, xx-rect_width/2-arrow_width, yy)
+ } else {
+ g.EchoArrowLine(NEXT, x-rect_width/2, yy, xx+rect_width/2+arrow_width, yy)
+ }
+ g.EchoText(TEXT, (x+xx)/2, yy, kit.Format(v[kit.MDB_TEXT]))
+ }
+
+ yy += rect_height / 2
+ if kit.Format(v[kit.MDB_PREV]) != "" {
+ xx := line_pos[kit.Int(v[kit.MDB_PREV])]
+ if x < xx {
+ g.EchoArrowLine(PREV, x+rect_width/2, yy, xx-rect_width/2-arrow_width, yy)
+ } else {
+ g.EchoArrowLine(PREV, x-rect_width/2, yy, xx+rect_width/2+arrow_width, yy)
+ }
+ g.EchoText(ECHO, (x+xx)/2, yy, kit.Format(v[kit.MDB_ECHO]))
+ }
+ }
+ }
+
+ g.Dump(m, HEAD).Dump(m, LINE)
+ g.Dump(m, RECT).Dump(m, NEXT).Dump(m, PREV)
+ g.Dump(m, TEXT).Dump(m, ECHO)
+ return s
+}
+
+const (
+ ARROW = "arrow"
+
+ HEAD = "head"
+ LINE = "line"
+ RECT = "rect"
+ NEXT = "next"
+ PREV = "prev"
+ TEXT = "text"
+ ECHO = "echo"
+)
+
+const SEQUENCE = "sequence"
+
+func init() {
+ wiki.AddChart(SEQUENCE, func(m *ice.Message) wiki.Chart {
+ m.Option(wiki.MARGINX, "60")
+ m.Option(wiki.MARGINY, "20")
+ m.Option(wiki.STROKE_WIDTH, "1")
+ m.Option(wiki.STROKE, cli.WHITE)
+ m.Option(wiki.FILL, cli.WHITE)
+ wiki.AddGroupOption(m, ARROW, wiki.HEIGHT, "8", wiki.WIDTH, "18", wiki.FILL, cli.GLASS)
+ wiki.AddGroupOption(m, HEAD, wiki.FILL, cli.GLASS)
+ wiki.AddGroupOption(m, LINE, wiki.STROKE_DASHARRAY, "20 4 4 4")
+ wiki.AddGroupOption(m, RECT, wiki.HEIGHT, "40", wiki.WIDTH, "14")
+ wiki.AddGroupOption(m, NEXT, wiki.FILL, cli.GLASS)
+ wiki.AddGroupOption(m, PREV, wiki.STROKE_DASHARRAY, "10 2")
+ wiki.AddGroupOption(m, TEXT, wiki.FONT_SIZE, "16")
+ wiki.AddGroupOption(m, ECHO, wiki.FONT_SIZE, "12")
+ return &Sequence{}
+ })
+}