chore(tui): delete dead braille code, hoist sparkWidth, stop resize flash
1. Delete braille.go + braille_test.go — dead code, only referenced by its own test. Can be re-added when latency charts are built. 2. Hoist duplicate `const sparkWidth = 40` (update.go + view_detail.go) to package-level `detailSparkWidth`. Click-index resolution and rendering now share one constant. 3. Remove tea.ClearScreen on every resize — caused full-screen flash during continuous resizes. ctrl+l manual clear kept.
This commit was merged in pull request #115.
This commit is contained in:
@@ -1,103 +0,0 @@
|
|||||||
package tui
|
|
||||||
|
|
||||||
// braillePlane is a subpixel canvas where each terminal cell maps to a 2×4
|
|
||||||
// dot grid, rendered via Unicode braille (U+2800..U+28FF).
|
|
||||||
type braillePlane struct {
|
|
||||||
wCells, hCells int
|
|
||||||
wDots, hDots int
|
|
||||||
dots []bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func newBraillePlane(wCells, hCells int) *braillePlane {
|
|
||||||
wd, hd := wCells*2, hCells*4
|
|
||||||
return &braillePlane{
|
|
||||||
wCells: wCells, hCells: hCells,
|
|
||||||
wDots: wd, hDots: hd,
|
|
||||||
dots: make([]bool, wd*hd),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *braillePlane) set(dx, dy int) {
|
|
||||||
if dx < 0 || dy < 0 || dx >= p.wDots || dy >= p.hDots {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p.dots[dy*p.wDots+dx] = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// line draws a Bresenham line between two dot coordinates.
|
|
||||||
func (p *braillePlane) line(x0, y0, x1, y1 int) {
|
|
||||||
dx := intAbs(x1 - x0)
|
|
||||||
sx := 1
|
|
||||||
if x0 >= x1 {
|
|
||||||
sx = -1
|
|
||||||
}
|
|
||||||
dy := -intAbs(y1 - y0)
|
|
||||||
sy := 1
|
|
||||||
if y0 >= y1 {
|
|
||||||
sy = -1
|
|
||||||
}
|
|
||||||
err := dx + dy
|
|
||||||
for {
|
|
||||||
p.set(x0, y0)
|
|
||||||
if x0 == x1 && y0 == y1 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
e2 := 2 * err
|
|
||||||
if e2 >= dy {
|
|
||||||
err += dy
|
|
||||||
x0 += sx
|
|
||||||
}
|
|
||||||
if e2 <= dx {
|
|
||||||
err += dx
|
|
||||||
y0 += sy
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// fillBelow fills all dots below the topmost lit dot in each column,
|
|
||||||
// producing an area-chart effect.
|
|
||||||
func (p *braillePlane) fillBelow() {
|
|
||||||
for x := 0; x < p.wDots; x++ {
|
|
||||||
topY := -1
|
|
||||||
for y := 0; y < p.hDots; y++ {
|
|
||||||
if p.dots[y*p.wDots+x] {
|
|
||||||
topY = y
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if topY >= 0 {
|
|
||||||
for y := topY + 1; y < p.hDots; y++ {
|
|
||||||
p.dots[y*p.wDots+x] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// cellMask builds the U+2800-relative bitmask for one terminal cell.
|
|
||||||
func (p *braillePlane) cellMask(cx, cy int) byte {
|
|
||||||
type bit struct {
|
|
||||||
dx, dy int
|
|
||||||
m byte
|
|
||||||
}
|
|
||||||
bits := [...]bit{
|
|
||||||
{0, 0, 0x01}, {0, 1, 0x02}, {0, 2, 0x04},
|
|
||||||
{1, 0, 0x08}, {1, 1, 0x10}, {1, 2, 0x20},
|
|
||||||
{0, 3, 0x40}, {1, 3, 0x80},
|
|
||||||
}
|
|
||||||
var mask byte
|
|
||||||
for _, b := range bits {
|
|
||||||
dx := cx*2 + b.dx
|
|
||||||
dy := cy*4 + b.dy
|
|
||||||
if dx >= 0 && dx < p.wDots && dy >= 0 && dy < p.hDots && p.dots[dy*p.wDots+dx] {
|
|
||||||
mask |= b.m
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mask
|
|
||||||
}
|
|
||||||
|
|
||||||
func intAbs(n int) int {
|
|
||||||
if n < 0 {
|
|
||||||
return -n
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
package tui
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestBraillePlane_Set(t *testing.T) {
|
|
||||||
p := newBraillePlane(2, 1)
|
|
||||||
if p.wDots != 4 || p.hDots != 4 {
|
|
||||||
t.Fatalf("expected 4x4 dots, got %dx%d", p.wDots, p.hDots)
|
|
||||||
}
|
|
||||||
p.set(0, 0)
|
|
||||||
if !p.dots[0] {
|
|
||||||
t.Error("dot at (0,0) should be set")
|
|
||||||
}
|
|
||||||
p.set(-1, 0) // out of bounds, should not panic
|
|
||||||
p.set(0, 99) // out of bounds, should not panic
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBraillePlane_CellMask(t *testing.T) {
|
|
||||||
p := newBraillePlane(1, 1)
|
|
||||||
// Set bottom-left dot
|
|
||||||
p.set(0, 3)
|
|
||||||
mask := p.cellMask(0, 0)
|
|
||||||
if mask != 0x40 {
|
|
||||||
t.Errorf("bottom-left dot should be 0x40, got 0x%02x", mask)
|
|
||||||
}
|
|
||||||
// Set all dots
|
|
||||||
for y := 0; y < 4; y++ {
|
|
||||||
for x := 0; x < 2; x++ {
|
|
||||||
p.set(x, y)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mask = p.cellMask(0, 0)
|
|
||||||
if mask != 0xFF {
|
|
||||||
t.Errorf("all dots should be 0xFF, got 0x%02x", mask)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBraillePlane_Line(t *testing.T) {
|
|
||||||
p := newBraillePlane(3, 1)
|
|
||||||
p.line(0, 2, 5, 2) // horizontal line
|
|
||||||
for x := 0; x <= 5; x++ {
|
|
||||||
if !p.dots[2*p.wDots+x] {
|
|
||||||
t.Errorf("dot at (%d, 2) should be set", x)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBraillePlane_FillBelow(t *testing.T) {
|
|
||||||
p := newBraillePlane(1, 1)
|
|
||||||
p.set(0, 1) // set dot at row 1
|
|
||||||
p.fillBelow()
|
|
||||||
if !p.dots[1*p.wDots+0] {
|
|
||||||
t.Error("original dot should still be set")
|
|
||||||
}
|
|
||||||
if !p.dots[2*p.wDots+0] {
|
|
||||||
t.Error("row 2 should be filled")
|
|
||||||
}
|
|
||||||
if !p.dots[3*p.wDots+0] {
|
|
||||||
t.Error("row 3 should be filled")
|
|
||||||
}
|
|
||||||
if p.dots[0*p.wDots+0] {
|
|
||||||
t.Error("row 0 above the dot should not be filled")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -80,6 +80,8 @@ const (
|
|||||||
chromeFooter = 2 // footer: "\n" prefix + text line
|
chromeFooter = 2 // footer: "\n" prefix + text line
|
||||||
chromeTable = 3 // renderTable "\n" prefix + top border + header + bottom border (lipgloss collapses two into three rendered lines)
|
chromeTable = 3 // renderTable "\n" prefix + top border + header + bottom border (lipgloss collapses two into three rendered lines)
|
||||||
chromeBase = chromePadV + chromeHeader + chromeGaps + chromeFooter + chromeTable
|
chromeBase = chromePadV + chromeHeader + chromeGaps + chromeFooter + chromeTable
|
||||||
|
|
||||||
|
detailSparkWidth = 40
|
||||||
)
|
)
|
||||||
|
|
||||||
type sessionState int
|
type sessionState int
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ func (m *Model) handleResize(msg tea.WindowSizeMsg) (tea.Model, tea.Cmd) {
|
|||||||
m.historyViewport.Height = msg.Height - 10
|
m.historyViewport.Height = msg.Height - 10
|
||||||
m.slaViewport.Width = msg.Width - chromePadH
|
m.slaViewport.Width = msg.Width - chromePadH
|
||||||
m.slaViewport.Height = msg.Height - 16
|
m.slaViewport.Height = msg.Height - 16
|
||||||
return m, tea.ClearScreen
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) handleTick(t time.Time) (tea.Model, tea.Cmd) {
|
func (m *Model) handleTick(t time.Time) (tea.Model, tea.Cmd) {
|
||||||
@@ -392,16 +392,14 @@ func (m *Model) handleSparklineClick(msg tea.MouseMsg) (tea.Model, tea.Cmd) {
|
|||||||
site := m.sites[m.cursor]
|
site := m.sites[m.cursor]
|
||||||
hist, _ := m.engine.GetHistory(site.ID)
|
hist, _ := m.engine.GetHistory(site.ID)
|
||||||
|
|
||||||
const sparkWidth = 40
|
|
||||||
|
|
||||||
if zi := m.zones.Get("spark-latency"); zi != nil && !zi.IsZero() && zi.InBounds(msg) {
|
if zi := m.zones.Get("spark-latency"); zi != nil && !zi.IsZero() && zi.InBounds(msg) {
|
||||||
x, _ := zi.Pos(msg)
|
x, _ := zi.Pos(msg)
|
||||||
m.sparkTooltipIdx = resolveSparklineIndex(x, sparkWidth, len(hist.Latencies))
|
m.sparkTooltipIdx = resolveSparklineIndex(x, detailSparkWidth, len(hist.Latencies))
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
if zi := m.zones.Get("spark-heartbeat"); zi != nil && !zi.IsZero() && zi.InBounds(msg) {
|
if zi := m.zones.Get("spark-heartbeat"); zi != nil && !zi.IsZero() && zi.InBounds(msg) {
|
||||||
x, _ := zi.Pos(msg)
|
x, _ := zi.Pos(msg)
|
||||||
m.sparkTooltipIdx = resolveSparklineIndex(x, sparkWidth, len(hist.Statuses))
|
m.sparkTooltipIdx = resolveSparklineIndex(x, detailSparkWidth, len(hist.Statuses))
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -207,9 +207,8 @@ func (m Model) viewDetailPanel() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
b.WriteString(m.divider() + "\n")
|
b.WriteString(m.divider() + "\n")
|
||||||
const sparkWidth = 40
|
|
||||||
if site.Type == "push" {
|
if site.Type == "push" {
|
||||||
b.WriteString(" " + m.zones.Mark("spark-heartbeat", m.heartbeatSparkline(hist.Statuses, sparkWidth, "")))
|
b.WriteString(" " + m.zones.Mark("spark-heartbeat", m.heartbeatSparkline(hist.Statuses, detailSparkWidth, "")))
|
||||||
if len(hist.Statuses) > 0 {
|
if len(hist.Statuses) > 0 {
|
||||||
up := 0
|
up := 0
|
||||||
for _, s := range hist.Statuses {
|
for _, s := range hist.Statuses {
|
||||||
@@ -222,7 +221,7 @@ func (m Model) viewDetailPanel() string {
|
|||||||
up, len(hist.Statuses))
|
up, len(hist.Statuses))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
b.WriteString(" " + m.zones.Mark("spark-latency", m.latencySparkline(hist.Latencies, hist.Statuses, sparkWidth, "")))
|
b.WriteString(" " + m.zones.Mark("spark-latency", m.latencySparkline(hist.Latencies, hist.Statuses, detailSparkWidth, "")))
|
||||||
var minL, maxL, total time.Duration
|
var minL, maxL, total time.Duration
|
||||||
count := 0
|
count := 0
|
||||||
for i, l := range hist.Latencies {
|
for i, l := range hist.Latencies {
|
||||||
@@ -249,7 +248,7 @@ func (m Model) viewDetailPanel() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if m.sparkTooltipIdx >= 0 {
|
if m.sparkTooltipIdx >= 0 {
|
||||||
b.WriteString("\n" + m.renderSparkTooltip(site, hist, sparkWidth))
|
b.WriteString("\n" + m.renderSparkTooltip(site, hist, detailSparkWidth))
|
||||||
}
|
}
|
||||||
|
|
||||||
b.WriteString("\n")
|
b.WriteString("\n")
|
||||||
|
|||||||
Reference in New Issue
Block a user