Split into category packages; module github.com/ZacharyZhang-NY/MujicaUI
This commit is contained in:
1822 files changed
+24568
-21314
No files matched your search
@@ -0,0 +1,132 @@
|
||||
package code
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||
)
|
||||
|
||||
// LogLevel is a log line's severity.
|
||||
type LogLevel int
|
||||
|
||||
const (
|
||||
LogDebug LogLevel = iota
|
||||
LogInfo
|
||||
LogWarn
|
||||
LogError
|
||||
)
|
||||
|
||||
// LogLine is one entry of a LogViewer.
|
||||
type LogLine struct {
|
||||
Time time.Time
|
||||
Level LogLevel
|
||||
Text string
|
||||
}
|
||||
|
||||
// LogViewerState is a LogViewer's filters, highlight and place.
|
||||
type LogViewerState struct {
|
||||
// Hidden levels are filtered out.
|
||||
Hidden [4]bool
|
||||
// Query is highlighted in the shown lines.
|
||||
Query string
|
||||
// Follow keeps the newest line in view; it starts on.
|
||||
Follow bool
|
||||
rows ui.ListState
|
||||
shown []int
|
||||
scanned int
|
||||
hidden [4]bool
|
||||
started bool
|
||||
}
|
||||
|
||||
// LogViewerOptions configure a LogViewer.
|
||||
type LogViewerOptions struct {
|
||||
Disabled bool
|
||||
}
|
||||
|
||||
// LogViewer shows filtered, highlighted log lines, virtualized.
|
||||
func LogViewer(c *ui.Context, lines []LogLine, s *LogViewerState, opts LogViewerOptions) core.ChoiceResult {
|
||||
if s == nil {
|
||||
panic("mujica: LogViewer needs a state")
|
||||
}
|
||||
if !s.started {
|
||||
s.started, s.Follow = true, true
|
||||
}
|
||||
var res core.ChoiceResult
|
||||
res.Element = CodePanel(c).Disabled(opts.Disabled)
|
||||
res.Element.Children(func() {
|
||||
CodeHeader(c, func() {
|
||||
for lv := LogDebug; lv <= LogError; lv++ {
|
||||
on := !s.Hidden[lv]
|
||||
if input.Toggle(c, &on, LogViewerName(c, lv), input.ToggleOptions{Disabled: opts.Disabled}).Changed() {
|
||||
s.Hidden[lv] = !on
|
||||
res = core.NewChoiceResult(res.Element, true)
|
||||
slog.Debug("mujica: log_viewer level", "level", lv, "shown", on)
|
||||
}
|
||||
}
|
||||
input.SearchInput(c, &s.Query, input.SearchInputOptions{Disabled: opts.Disabled, Placeholder: core.Msg(c, "log_viewer.highlight")}).Element.Grow(1).MinWidth(0)
|
||||
was := s.Follow
|
||||
if input.Toggle(c, &s.Follow, core.Msg(c, "log_viewer.follow"), input.ToggleOptions{Disabled: opts.Disabled}).Changed() {
|
||||
res = core.NewChoiceResult(res.Element, true)
|
||||
slog.Debug("mujica: log_viewer follow", "on", s.Follow)
|
||||
if s.Follow && !was {
|
||||
s.rows.ScrollToEnd()
|
||||
}
|
||||
}
|
||||
})
|
||||
logViewerFilter(lines, s)
|
||||
if len(s.shown) == 0 {
|
||||
CodeCaption(c, core.Msg(c, "log_viewer.empty")).Padding(core.Density(c).Unit() * 4)
|
||||
return
|
||||
}
|
||||
s.rows.FollowEnd = s.Follow
|
||||
s.rows.Key = func(i int) any { return s.shown[i] }
|
||||
s.rows.Label = func(i int) string { return lines[s.shown[i]].Text }
|
||||
d := core.Density(c)
|
||||
ui.List(c, &s.rows, len(s.shown), func(i int) {
|
||||
logViewerRow(c, lines[s.shown[i]], s.Query)
|
||||
}).Grow(1).MinHeight(d.ControlHeight()*3).Padding(d.Unit(), 0)
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
// logViewerFilter extends or redoes the shown lines.
|
||||
func logViewerFilter(lines []LogLine, s *LogViewerState) {
|
||||
if s.hidden != s.Hidden || s.scanned > len(lines) {
|
||||
s.hidden, s.scanned, s.shown = s.Hidden, 0, s.shown[:0]
|
||||
}
|
||||
for i := s.scanned; i < len(lines); i++ {
|
||||
lv := lines[i].Level
|
||||
if lv < LogDebug || lv > LogError {
|
||||
panic("mujica: LogLine level out of range")
|
||||
}
|
||||
if !s.Hidden[lv] {
|
||||
s.shown = append(s.shown, i)
|
||||
}
|
||||
}
|
||||
s.scanned = len(lines)
|
||||
}
|
||||
|
||||
func LogViewerName(c *ui.Context, lv LogLevel) string {
|
||||
return core.Msg(c, [...]string{"log_viewer.debug", "log_viewer.info", "log_viewer.warn", "log_viewer.error"}[lv])
|
||||
}
|
||||
|
||||
func logViewerRow(c *ui.Context, l LogLine, query string) {
|
||||
k := core.Tokens(c)
|
||||
d := core.Density(c)
|
||||
col := [...]ui.Color{k.TextMuted, k.Info, k.Warning, k.Danger}[l.Level]
|
||||
ui.Row(c).MinHeight(d.ControlHeight()).Padding(0, d.Unit()*3).Gap(d.Unit() * 3).AlignItems(ui.Center).Children(func() {
|
||||
if !l.Time.IsZero() {
|
||||
CodeMono(c, l.Time.Format("15:04:05.000")).TextColor(k.TextMuted).Shrink(0)
|
||||
}
|
||||
ui.Text(c, fmt.Sprintf("%-5s", LogViewerName(c, l.Level))).Font(theme.MonoFont).FontSize(core.FontSize(c, CodeSize)).
|
||||
FontWeight(700).TextColor(col).SingleLine().Shrink(0)
|
||||
ui.RichText(c, CodeMatchSpans(c, l.Text, query)...).Font(theme.MonoFont).FontSize(core.FontSize(c, CodeSize)).
|
||||
SingleLine().Grow(1).MinWidth(0)
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user