Make dashboard updateable
This commit is contained in:
86
terminal_cursor.go
Normal file
86
terminal_cursor.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/charmbracelet/x/ansi"
|
||||
)
|
||||
|
||||
// terminalCursorOutput decorates Bubble Tea's completed frame writes with a
|
||||
// hardware cursor position. Bubble Tea otherwise parks the cursor at the
|
||||
// bottom of every frame, which prevents a real insertion caret inside a custom
|
||||
// editor.
|
||||
type terminalCursorOutput struct {
|
||||
file *os.File
|
||||
|
||||
mu sync.Mutex
|
||||
visible bool
|
||||
column int
|
||||
row int
|
||||
}
|
||||
|
||||
func newTerminalCursorOutput(file *os.File) *terminalCursorOutput {
|
||||
return &terminalCursorOutput{file: file}
|
||||
}
|
||||
|
||||
func (o *terminalCursorOutput) SetCursor(visible bool, column, row int) {
|
||||
o.mu.Lock()
|
||||
defer o.mu.Unlock()
|
||||
o.visible, o.column, o.row = visible, column, row
|
||||
}
|
||||
|
||||
func (o *terminalCursorOutput) FrameMarker() string {
|
||||
o.mu.Lock()
|
||||
defer o.mu.Unlock()
|
||||
if !o.visible {
|
||||
return ""
|
||||
}
|
||||
// This zero-width sequence makes frames at different insertion positions
|
||||
// distinct, preventing Bubble Tea from skipping a hardware-cursor-only
|
||||
// update. The output wrapper reasserts the same position after Bubble Tea
|
||||
// parks its cursor at the bottom of the frame.
|
||||
return ansi.CursorPosition(o.column, o.row)
|
||||
}
|
||||
|
||||
func (o *terminalCursorOutput) Write(value []byte) (int, error) {
|
||||
o.mu.Lock()
|
||||
defer o.mu.Unlock()
|
||||
|
||||
written, err := o.file.Write(value)
|
||||
if err != nil || written != len(value) {
|
||||
return written, err
|
||||
}
|
||||
// Let Bubble Tea restore the cursor normally during startup/shutdown.
|
||||
if bytes.Equal(value, []byte(ansi.ShowCursor)) || bytes.Equal(value, []byte(ansi.HideCursor)) {
|
||||
if bytes.Equal(value, []byte(ansi.ShowCursor)) {
|
||||
_, _ = io.WriteString(o.file, ansi.SetCursorStyle(0))
|
||||
}
|
||||
return written, nil
|
||||
}
|
||||
if !o.visible {
|
||||
_, err = io.WriteString(o.file, ansi.HideCursor)
|
||||
return written, err
|
||||
}
|
||||
_, err = io.WriteString(
|
||||
o.file,
|
||||
ansi.SetCursorStyle(5)+
|
||||
ansi.CursorPosition(o.column, o.row)+
|
||||
ansi.ShowCursor,
|
||||
)
|
||||
return written, err
|
||||
}
|
||||
|
||||
func (o *terminalCursorOutput) Read(value []byte) (int, error) {
|
||||
return o.file.Read(value)
|
||||
}
|
||||
|
||||
func (o *terminalCursorOutput) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *terminalCursorOutput) Fd() uintptr {
|
||||
return o.file.Fd()
|
||||
}
|
||||
Reference in New Issue
Block a user