package tui import ( "fmt" "get-container/utils" "regexp" "strconv" "strings" "github.com/charmbracelet/bubbles/progress" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "github.com/lucasb-eyer/go-colorful" "github.com/muesli/gamut" "github.com/muesli/termenv" ) type ModelDCUInfo struct { width, height int // 屏幕尺寸 pro progress.Model // 进度条 proLowLeight progress.Model // 低亮度进度条 proWidth int // 进度条宽度 modMsg *ModelMsg // 缓存的模型信息 actionMsg *ActionMsg // 缓存的动作信息 darkModule bool // 是否为黑暗模式 top, middle, bottom string // 缓存的边框 topL, middleL, bottomL string // 缓存的低亮度边框 subLin int // 边框更新标志 } const ( TopLine = `╞═══════════════════════════════╪══════════════════════╪══════════════════════╪` MiddleLine = `├───────────────────────────────┼──────────────────────┼──────────────────────┼` BottomLine = `╞═══════════════════════════════╧══════════════════════╧══════════════════════╪` OtherWidth = 8 StaticWidth = 79 // 固定表格的宽度 ProgressMinWidth = 15 // 进度条最小宽度 ) var ( ReDCUName = regexp.MustCompile(`(?i)^[A-Z0-9-_]*`) ) func NewModelDCUInfo(w, h int) *ModelDCUInfo { return &ModelDCUInfo{width: w, height: h, darkModule: false} } func (m *ModelDCUInfo) Init() tea.Cmd { if m.width < StaticWidth+ProgressMinWidth+OtherWidth { return tea.Quit } m.proWidth = ProgressMinWidth if m.width > StaticWidth+ProgressMinWidth+OtherWidth { m.proWidth = m.width - OtherWidth - StaticWidth } m.pro = progress.New(progress.WithColorProfile(termenv.TrueColor), progress.WithGradient("#0000ffff", "#ff0000ff"), progress.WithWidth(m.proWidth-5), progress.WithoutPercentage()) c1, _ := colorful.MakeColor(gamut.Darker(lipgloss.Color("#0000ffff"), 0.8)) c2, _ := colorful.MakeColor(gamut.Darker(lipgloss.Color("#ff0000ff"), 0.8)) m.proLowLeight = progress.New(progress.WithColorProfile(termenv.TrueColor), progress.WithGradient(c1.Hex(), c2.Hex()), progress.WithWidth(m.proWidth-5), progress.WithoutPercentage()) return nil } func (m *ModelDCUInfo) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) { switch msg := inputMsg.(type) { case *ModelMsg: m.modMsg = msg return m, nil case tea.WindowSizeMsg: m.height = msg.Height m.width = msg.Width return m, nil case *ActionMsg: m.actionMsg = msg if msg.Action != nil && msg.VM == VMMain { m.darkModule = true } else { m.darkModule = false } return m, nil } return m, nil } func (m *ModelDCUInfo) View() string { if m.modMsg == nil { return "" } lineWidth := 0 qmap, qlock := m.modMsg.DCUInfo.GetQuitInfo() smap, slock := m.modMsg.DCUInfo.GetSlowInfo() strBuilder := strings.Builder{} strCache := strings.Builder{} var targetDCU int = -1 if m.actionMsg != nil && m.actionMsg.TargetDCUIndex != nil { targetDCU = *m.actionMsg.TargetDCUIndex } var borderStr string = myBorder.Left if m.darkModule { borderStr = LowLeightStyle.Render(borderStr) } infos := make([]string, 0) for i := range 64 { qinfo, haveq := qmap[i] sinfo, haves := smap[i] if !(haveq || haves) { continue } isLowStyle := (targetDCU != qinfo.Id && targetDCU != -1) || m.darkModule strBuilder.WriteString(borderStr) strCache.WriteByte(' ') strCache.WriteString(FormatStr(strconv.Itoa(qinfo.Id), 4, lipgloss.Left)) strCache.WriteByte(' ') strCache.WriteString(FormatStr(ReDCUName.FindString(qinfo.Name), 8, lipgloss.Left)) strCache.WriteByte(' ') strCache.WriteString(FormatStr(qinfo.PerformanceLevel, 15, lipgloss.Right)) strCache.WriteByte(' ') if isLowStyle { str := strCache.String() strBuilder.WriteString(LowLeightStyle.Render(str)) } else { strBuilder.WriteString(strCache.String()) } strCache.Reset() strBuilder.WriteString(borderStr) strCache.WriteByte(' ') strCache.WriteString(FormatStr(qinfo.BusId, 13, lipgloss.Left)) strCache.WriteByte(' ') strCache.WriteString(FormatStr("Off", 6, lipgloss.Right)) strCache.WriteByte(' ') if isLowStyle { str := strCache.String() strBuilder.WriteString(LowLeightStyle.Render(str)) } else { strBuilder.WriteString(strCache.String()) } strCache.Reset() strCache.WriteByte(' ') strCache.WriteString(FormatStr("Off", 16, lipgloss.Left)) strBuilder.WriteString(borderStr) strCache.WriteByte(' ') if sinfo.Ecc.Load() { strCache.WriteString(FormatStr("On", 3, lipgloss.Right)) } else { strCache.WriteString(FormatStr("Off", 3, lipgloss.Right)) } strCache.WriteByte(' ') if isLowStyle { str := strCache.String() strBuilder.WriteString(LowLeightStyle.Render(str)) } else { strBuilder.WriteString(strCache.String()) } strCache.Reset() strBuilder.WriteString(borderStr) if isLowStyle { strBuilder.WriteString(LowLeightStyle.Render(" DCU: ")) strBuilder.WriteString(m.proLowLeight.ViewAs(float64(qinfo.DCUUTil / 100))) strBuilder.WriteString(LowLeightStyle.Render(fmt.Sprintf(" %3d%% ", int(qinfo.DCUUTil)))) } else { strBuilder.WriteString(" DCU: ") strBuilder.WriteString(m.pro.ViewAs(float64(qinfo.DCUUTil / 100))) strBuilder.WriteString(fmt.Sprintf(" %3d%% ", int(qinfo.DCUUTil))) } strBuilder.WriteString(borderStr) strBuilder.WriteByte('\n') strBuilder.WriteString(borderStr) strCache.WriteByte(' ') strCache.WriteString(FormatStr(qinfo.Fan, 4, lipgloss.Left)) strCache.WriteByte(' ') strCache.WriteString(FormatStr(fmt.Sprintf("%.1fC", qinfo.Temp), 6, lipgloss.Left)) strCache.WriteByte(' ') strCache.WriteString(FormatStr(fmt.Sprintf("%.1fW / %.1fW", qinfo.PwrAvg, qinfo.PwrCap), 17, lipgloss.Right)) strCache.WriteByte(' ') if isLowStyle { str := strCache.String() strBuilder.WriteString(LowLeightStyle.Render(str)) } else { strBuilder.WriteString(strCache.String()) } strCache.Reset() strBuilder.WriteString(borderStr) if isLowStyle { strCache.WriteByte(' ') strCache.WriteString(FormatStr(fmt.Sprintf("%dMiB / %dMiB", qinfo.MemUsed/uint64(utils.MiB), qinfo.MemTotal/uint64(utils.MiB)), 20, lipgloss.Right)) strCache.WriteByte(' ') strBuilder.WriteString(LowLeightStyle.Render(strCache.String())) strCache.Reset() } else { strBuilder.WriteByte(' ') strBuilder.WriteString(FormatStr(fmt.Sprintf("%dMiB / %dMiB", qinfo.MemUsed/uint64(utils.MiB), qinfo.MemTotal/uint64(utils.MiB)), 20, lipgloss.Right)) strBuilder.WriteByte(' ') } strBuilder.WriteString(borderStr) if isLowStyle { strCache.WriteByte(' ') strCache.WriteString(FormatStr(fmt.Sprintf("%.1f%%", qinfo.DCUUTil), 6, lipgloss.Left)) strCache.WriteByte(' ') strCache.WriteString(FormatStr(sinfo.PwrMode.Load().(string), 13, lipgloss.Right)) strCache.WriteByte(' ') strBuilder.WriteString(LowLeightStyle.Render(strCache.String())) strCache.Reset() } else { strBuilder.WriteByte(' ') strBuilder.WriteString(FormatStr(fmt.Sprintf("%.1f%%", qinfo.DCUUTil), 6, lipgloss.Left)) strBuilder.WriteByte(' ') strBuilder.WriteString(FormatStr(sinfo.PwrMode.Load().(string), 13, lipgloss.Right)) strBuilder.WriteByte(' ') } strBuilder.WriteString(borderStr) if isLowStyle { strCache.WriteString(" Mem: ") strCache.WriteString(m.proLowLeight.ViewAs(float64(qinfo.MemUsed) / float64(qinfo.MemTotal))) strCache.WriteString(LowLeightStyle.Render(fmt.Sprintf(" %3d%% ", int(100*float64(qinfo.MemUsed)/float64(qinfo.MemTotal))))) strBuilder.WriteString(LowLeightStyle.Render(strCache.String())) strCache.Reset() } else { strBuilder.WriteString(" Mem: ") strBuilder.WriteString(m.pro.ViewAs(float64(qinfo.MemUsed) / float64(qinfo.MemTotal))) strBuilder.WriteByte(' ') strBuilder.WriteString(fmt.Sprintf("%3d%%", int(100*float64(qinfo.MemUsed)/float64(qinfo.MemTotal)))) strBuilder.WriteByte(' ') } strBuilder.WriteString(borderStr) strBuilder.WriteByte('\n') infos = append(infos, strBuilder.String()) strBuilder.Reset() } slock.Unlock() qlock.Unlock() if len(infos) > 0 { lineWidth = lipgloss.Width(infos[0]) } subLen := max(lineWidth-StaticWidth-1, 0) if subLen != m.subLin { top := TopLine + strings.Repeat("═", subLen) + "╕" middle := MiddleLine + strings.Repeat("─", subLen) + "┤" bottom := BottomLine + strings.Repeat("═", subLen) + "╡" m.topL = LowLeightStyle.Render(top) + "\n" m.middleL = LowLeightStyle.Render(middle) + "\n" m.bottomL = LowLeightStyle.Render(bottom) + "\n" m.top = top + "\n" m.middle = middle + "\n" m.bottom = bottom + "\n" } if m.darkModule { return m.topL + strings.Join(infos, m.middleL) + m.bottomL } else { return m.top + strings.Join(infos, m.middle) + m.bottom } }