From 8f6203e6ed243b87e6628dde6457ea809591ad11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Mon, 5 Oct 2026 15:01:29 -0400 Subject: [PATCH] wip-devtools --- messages_devtools.go | 8 + schema_tree.go | 155 ++++++++++++++++++ schema_tree_test.go | 90 ++++++++++ .../snapshots/darwin/schema_tree-dark.png | Bin 0 -> 16250 bytes .../snapshots/darwin/schema_tree-light.png | Bin 0 -> 15825 bytes 5 files changed, 253 insertions(+) create mode 100644 schema_tree.go create mode 100644 schema_tree_test.go create mode 100644 testdata/snapshots/darwin/schema_tree-dark.png create mode 100644 testdata/snapshots/darwin/schema_tree-light.png diff --git a/messages_devtools.go b/messages_devtools.go index 5a02788..57d55fd 100644 --- a/messages_devtools.go +++ b/messages_devtools.go @@ -87,6 +87,14 @@ func init() { "json_editor.valid": {"Valid JSON", "JSON 有效"}, "json_editor.at": {"Error at line %d, column %d", "错误位于第 %d 行第 %d 列"}, + "schema_tree.label": {"Schema", "数据库结构"}, + "schema_tree.empty": {"No databases", "没有数据库"}, + "schema_tree.database": {"Database", "数据库"}, + "schema_tree.table": {"Table", "表"}, + "schema_tree.view": {"View", "视图"}, + "schema_tree.column": {"Column", "列"}, + "schema_tree.index": {"Index", "索引"}, + "alert_list.empty": {"No alerts", "没有告警"}, }) } diff --git a/schema_tree.go b/schema_tree.go new file mode 100644 index 0000000..0c8fed3 --- /dev/null +++ b/schema_tree.go @@ -0,0 +1,155 @@ +package mujica + +import ( + "fmt" + "log/slog" + + "github.com/egoist/mygo/ui" + + "mujicaui/internal" + "mujicaui/theme" +) + +// SchemaKind is what a schema node is. The zero value is SchemaDatabase. +type SchemaKind int + +const ( + SchemaDatabase SchemaKind = iota + SchemaTable + SchemaView + SchemaColumn + SchemaIndex +) + +// SchemaNode is one database object; Detail is a column type or index columns. +type SchemaNode struct { + Name string + Kind SchemaKind + Detail string + Children []SchemaNode +} + +// SchemaTreeState is a SchemaTree's open nodes and choice. +type SchemaTreeState struct { + outline ui.OutlineState[string] + selected int + nodes map[string]*SchemaNode + kids map[string][]string + roots []string + src []SchemaNode +} + +// Selected returns the chosen node's path, as "shop/orders/id", and whether one is chosen. +func (s *SchemaTreeState) Selected() (string, bool) { + if s.selected < 0 || s.selected >= s.outline.Rows() { + return "", false + } + return s.outline.Item(s.selected), true +} + +// SchemaTreeOptions configure a SchemaTree. +type SchemaTreeOptions struct { + // Height of the tree; zero means 360. + Height float32 +} + +// SchemaTree shows databases, tables, views, columns and indexes as a keyboard tree, virtualized. +func SchemaTree(c *ui.Context, s *SchemaTreeState, roots []SchemaNode, opts SchemaTreeOptions) ChoiceResult { + if s == nil || opts.Height < 0 { + panic("mujica: SchemaTree needs a state and Height >= 0") + } + h := opts.Height + if h == 0 { + h = 360 + } + schemaIndex(s, roots) + k := Tokens(c) + d := Density(c) + var r ChoiceResult + r.Element = codePanel(c).Height(h).Label(msg(c, "schema_tree.label")) + r.Element.Children(func() { + if len(s.roots) == 0 { + codeCaption(c, msg(c, "schema_tree.empty")).Padding(d.Unit() * 4) + return + } + was := s.selected + s.outline.List.Selected = &s.selected + s.outline.List.Label = func(i int) string { return s.nodes[s.outline.Item(i)].Name } + restore := dataSelectTheme(c, 0) + e := ui.Outline(c, &s.outline, s.roots, func(p string) []string { return s.kids[p] }, func(p string) { + n := s.nodes[p] + ui.Row(c).Grow(1).MinWidth(0).MinHeight(d.ControlHeight()-2*d.Unit()).Gap(d.Unit()*2).AlignItems(ui.Center). + Label(fmt.Sprintf("%s %s %s", schemaWord(c, n.Kind), n.Name, n.Detail)).Children(func() { + sel, _ := s.Selected() + codeSelectMark(c, sel == p) + letter, col := schemaBadge(k, n.Kind) + codeBadge(c, letter, col) + ui.Text(c, n.Name).SingleLine().Shrink(0) + if n.Detail != "" { + codeMono(c, n.Detail).TextColor(k.TextMuted).MinWidth(0) + } + }) + }).Grow(1) + restore() + internal.Ring(e, k.Focus, theme.ControlRadius) + r.changed = s.selected != was + }) + if r.changed { + p, _ := s.Selected() + slog.Debug("mujica: schema_tree select", "path", p) + } + return r +} + +// schemaIndex maps paths to nodes once per new schema. +func schemaIndex(s *SchemaTreeState, roots []SchemaNode) { + if s.nodes != nil && len(roots) == len(s.src) && (len(roots) == 0 || &roots[0] == &s.src[0]) { + return + } + if s.nodes == nil { + s.selected = -1 // nothing chosen at first + } + s.src, s.nodes, s.kids, s.roots = roots, map[string]*SchemaNode{}, map[string][]string{}, nil + var walk func(list []SchemaNode, parent string) []string + walk = func(list []SchemaNode, parent string) []string { + var paths []string + for i := range list { + n := &list[i] + if n.Name == "" || n.Kind < SchemaDatabase || n.Kind > SchemaIndex { + panic(fmt.Sprintf("mujica: SchemaTree node %q needs a Name and a known Kind", n.Name)) + } + p := n.Name + if parent != "" { + p = parent + "/" + n.Name + } + if _, dup := s.nodes[p]; dup { + panic("mujica: SchemaTree has two nodes at " + p) + } + s.nodes[p] = n + paths = append(paths, p) + if len(n.Children) > 0 { + s.kids[p] = walk(n.Children, p) + } + } + return paths + } + s.roots = walk(roots, "") +} + +func schemaBadge(k theme.Tokens, kind SchemaKind) (string, ui.Color) { + switch kind { + case SchemaDatabase: + return "D", k.AccentText + case SchemaTable: + return "T", k.Info + case SchemaView: + return "V", k.Success + case SchemaIndex: + return "I", k.Warning + } + return "C", k.TextMuted +} + +func schemaWord(c *ui.Context, kind SchemaKind) string { + return msg(c, [...]string{"schema_tree.database", "schema_tree.table", "schema_tree.view", "schema_tree.column", "schema_tree.index"}[kind]) +} diff --git a/schema_tree_test.go b/schema_tree_test.go new file mode 100644 index 0000000..2a487b1 --- /dev/null +++ b/schema_tree_test.go @@ -0,0 +1,90 @@ +package mujica + +import ( + "fmt" + "testing" + + "github.com/egoist/mygo/ui" +) + +var schemaSample = []SchemaNode{{Name: "court", Kind: SchemaDatabase, Children: []SchemaNode{ + {Name: "guests", Kind: SchemaTable, Children: []SchemaNode{ + {Name: "id", Kind: SchemaColumn, Detail: "bigint primary key"}, + {Name: "name", Kind: SchemaColumn, Detail: "text not null"}, + {Name: "guests_name_idx", Kind: SchemaIndex, Detail: "(name)"}, + }}, + {Name: "seating", Kind: SchemaView}, +}}} + +func schemaTester(s *SchemaTreeState, roots []SchemaNode, changes *int) *ui.Tester { + return ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + if SchemaTree(c, s, roots, SchemaTreeOptions{}).Changed() { + *changes++ + } + }, 520, 420) +} + +func TestSchemaTreeKeyboardOpenAndChoose(t *testing.T) { + var s SchemaTreeState + n := 0 + tt := schemaTester(&s, schemaSample, &n) + if _, ok := s.Selected(); ok || tt.HasText("guests") { + t.Fatalf("start: %q", tt.Texts()) + } + tt.Key(0, ui.KeyTab) + tt.Key(0, ui.KeyDown) // court + tt.Key(0, ui.KeyRight) + tt.Key(0, ui.KeyDown) // guests + tt.Key(0, ui.KeyRight) + for _, s := range []string{"guests", "seating", "bigint primary key", "(name)"} { + if !tt.HasText(s) { + t.Fatalf("missing %q in %q", s, tt.Texts()) + } + } + tt.Key(0, ui.KeyDown) + if p, _ := s.Selected(); p != "court/guests/id" || n == 0 { + t.Fatalf("selected %q changes %d", p, n) + } +} + +func TestSchemaTreeEmptyRulesMany(t *testing.T) { + var s SchemaTreeState + n := 0 + tt := schemaTester(&s, nil, &n) + if !tt.HasText("No databases") { + t.Fatal("empty text") + } + cols := make([]SchemaNode, 100000) + for i := range cols { + cols[i] = SchemaNode{Name: fmt.Sprint("c", i), Kind: SchemaColumn} + } + big := []SchemaNode{{Name: "wide", Kind: SchemaTable, Children: cols}} + var bs SchemaTreeState + tt = schemaTester(&bs, big, &n) + tt.Key(0, ui.KeyTab) + tt.Key(0, ui.KeyDown) + tt.Key(0, ui.KeyRight) + if got := len(tt.Texts()); got > 400 || !tt.HasText("c0") { + t.Fatalf("%d texts for 100000 columns", got) + } + defer func() { + if recover() == nil { + t.Fatal("duplicate path did not panic") + } + }() + var ds SchemaTreeState + schemaTester(&ds, []SchemaNode{{Name: "a"}, {Name: "a"}}, &n) +} + +func TestSchemaTreeSnapshot(t *testing.T) { + var s SchemaTreeState + snapshotAfter(t, "schema_tree", 500, 300, func(c *ui.Context) { + ui.Column(c).Padding(16).Children(func() { SchemaTree(c, &s, schemaSample, SchemaTreeOptions{Height: 260}) }) + }, func(tt *ui.Tester) { + s = SchemaTreeState{} + for _, k := range []ui.Key{ui.KeyTab, ui.KeyDown, ui.KeyRight, ui.KeyDown, ui.KeyRight, ui.KeyDown} { + tt.Key(0, k) + } + }) +} diff --git a/testdata/snapshots/darwin/schema_tree-dark.png b/testdata/snapshots/darwin/schema_tree-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..b11c6b27e8803791ed7d9910d8554d0e716d912c GIT binary patch literal 16250 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