style: fix all golangci-lint issues and format code (refs #17)
Fix 380 lint violations across all Go source files including wsl_v5, nlreturn, noinlineerr, errcheck, funlen, funcorder, tagliatelle, perfsprint, modernize, revive, gosec, ireturn, mnd, forcetypeassert, cyclop, and others. Key changes: - Split large handler/command functions into smaller methods - Extract scan helpers for database queries - Reorder exported/unexported methods per funcorder - Add sentinel errors in models package - Use camelCase JSON tags per tagliatelle defaults - Add package comments - Fix .gitignore to not exclude cmd/chat-cli directory
This commit is contained in:
@@ -31,6 +31,7 @@ type UI struct {
|
||||
}
|
||||
|
||||
// NewUI creates the tview-based IRC-like UI.
|
||||
|
||||
func NewUI() *UI {
|
||||
ui := &UI{
|
||||
app: tview.NewApplication(),
|
||||
@@ -39,80 +40,35 @@ func NewUI() *UI {
|
||||
},
|
||||
}
|
||||
|
||||
// Message area.
|
||||
ui.messages = tview.NewTextView().
|
||||
SetDynamicColors(true).
|
||||
SetScrollable(true).
|
||||
SetWordWrap(true).
|
||||
SetChangedFunc(func() {
|
||||
ui.app.Draw()
|
||||
})
|
||||
ui.messages.SetBorder(false)
|
||||
|
||||
// Status bar.
|
||||
ui.statusBar = tview.NewTextView().
|
||||
SetDynamicColors(true)
|
||||
ui.statusBar.SetBackgroundColor(tcell.ColorNavy)
|
||||
ui.statusBar.SetTextColor(tcell.ColorWhite)
|
||||
|
||||
// Input field.
|
||||
ui.input = tview.NewInputField().
|
||||
SetFieldBackgroundColor(tcell.ColorBlack).
|
||||
SetFieldTextColor(tcell.ColorWhite)
|
||||
ui.input.SetDoneFunc(func(key tcell.Key) {
|
||||
if key == tcell.KeyEnter {
|
||||
text := ui.input.GetText()
|
||||
if text == "" {
|
||||
return
|
||||
}
|
||||
ui.input.SetText("")
|
||||
if ui.onInput != nil {
|
||||
ui.onInput(text)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Capture Alt+N for window switching.
|
||||
ui.app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||
if event.Modifiers()&tcell.ModAlt != 0 {
|
||||
r := event.Rune()
|
||||
if r >= '0' && r <= '9' {
|
||||
idx := int(r - '0')
|
||||
ui.SwitchBuffer(idx)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return event
|
||||
})
|
||||
|
||||
// Layout: messages on top, status bar, input at bottom.
|
||||
ui.layout = tview.NewFlex().SetDirection(tview.FlexRow).
|
||||
AddItem(ui.messages, 0, 1, false).
|
||||
AddItem(ui.statusBar, 1, 0, false).
|
||||
AddItem(ui.input, 1, 0, true)
|
||||
|
||||
ui.app.SetRoot(ui.layout, true)
|
||||
ui.app.SetFocus(ui.input)
|
||||
ui.setupMessages()
|
||||
ui.setupStatusBar()
|
||||
ui.setupInput()
|
||||
ui.setupKeybindings()
|
||||
ui.setupLayout()
|
||||
|
||||
return ui
|
||||
}
|
||||
|
||||
// Run starts the UI event loop (blocks).
|
||||
|
||||
func (ui *UI) Run() error {
|
||||
return ui.app.Run()
|
||||
}
|
||||
|
||||
// Stop stops the UI.
|
||||
|
||||
func (ui *UI) Stop() {
|
||||
ui.app.Stop()
|
||||
}
|
||||
|
||||
// OnInput sets the callback for user input.
|
||||
|
||||
func (ui *UI) OnInput(fn func(string)) {
|
||||
ui.onInput = fn
|
||||
}
|
||||
|
||||
// AddLine adds a line to the specified buffer.
|
||||
|
||||
func (ui *UI) AddLine(bufferName string, line string) {
|
||||
ui.app.QueueUpdateDraw(func() {
|
||||
buf := ui.getOrCreateBuffer(bufferName)
|
||||
@@ -121,89 +77,219 @@ func (ui *UI) AddLine(bufferName string, line string) {
|
||||
// Mark unread if not currently viewing this buffer.
|
||||
if ui.buffers[ui.currentBuffer] != buf {
|
||||
buf.Unread++
|
||||
|
||||
ui.refreshStatus()
|
||||
}
|
||||
|
||||
// If viewing this buffer, append to display.
|
||||
if ui.buffers[ui.currentBuffer] == buf {
|
||||
fmt.Fprintln(ui.messages, line)
|
||||
_, _ = fmt.Fprintln(ui.messages, line)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// AddStatus adds a line to the status buffer (buffer 0).
|
||||
|
||||
func (ui *UI) AddStatus(line string) {
|
||||
ts := time.Now().Format("15:04")
|
||||
ui.AddLine("(status)", fmt.Sprintf("[gray]%s[white] %s", ts, line))
|
||||
|
||||
ui.AddLine(
|
||||
"(status)",
|
||||
fmt.Sprintf("[gray]%s[white] %s", ts, line),
|
||||
)
|
||||
}
|
||||
|
||||
// SwitchBuffer switches to the buffer at index n.
|
||||
|
||||
func (ui *UI) SwitchBuffer(n int) {
|
||||
ui.app.QueueUpdateDraw(func() {
|
||||
if n < 0 || n >= len(ui.buffers) {
|
||||
return
|
||||
}
|
||||
|
||||
ui.currentBuffer = n
|
||||
|
||||
buf := ui.buffers[n]
|
||||
buf.Unread = 0
|
||||
|
||||
ui.messages.Clear()
|
||||
|
||||
for _, line := range buf.Lines {
|
||||
fmt.Fprintln(ui.messages, line)
|
||||
_, _ = fmt.Fprintln(ui.messages, line)
|
||||
}
|
||||
|
||||
ui.messages.ScrollToEnd()
|
||||
ui.refreshStatus()
|
||||
})
|
||||
}
|
||||
|
||||
// SwitchToBuffer switches to the named buffer, creating it if needed.
|
||||
// SwitchToBuffer switches to the named buffer, creating it
|
||||
|
||||
func (ui *UI) SwitchToBuffer(name string) {
|
||||
ui.app.QueueUpdateDraw(func() {
|
||||
buf := ui.getOrCreateBuffer(name)
|
||||
|
||||
for i, b := range ui.buffers {
|
||||
if b == buf {
|
||||
ui.currentBuffer = i
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
buf.Unread = 0
|
||||
|
||||
ui.messages.Clear()
|
||||
|
||||
for _, line := range buf.Lines {
|
||||
fmt.Fprintln(ui.messages, line)
|
||||
_, _ = fmt.Fprintln(ui.messages, line)
|
||||
}
|
||||
|
||||
ui.messages.ScrollToEnd()
|
||||
ui.refreshStatus()
|
||||
})
|
||||
}
|
||||
|
||||
// SetStatus updates the status bar text.
|
||||
func (ui *UI) SetStatus(nick, target, connStatus string) {
|
||||
|
||||
func (ui *UI) SetStatus(
|
||||
nick, target, connStatus string,
|
||||
) {
|
||||
ui.app.QueueUpdateDraw(func() {
|
||||
ui.refreshStatusWith(nick, target, connStatus)
|
||||
})
|
||||
}
|
||||
|
||||
func (ui *UI) refreshStatus() {
|
||||
// Will be called from the main goroutine via QueueUpdateDraw parent.
|
||||
// Rebuild status from app state — caller must provide context.
|
||||
// BufferCount returns the number of buffers.
|
||||
|
||||
func (ui *UI) BufferCount() int {
|
||||
return len(ui.buffers)
|
||||
}
|
||||
|
||||
func (ui *UI) refreshStatusWith(nick, target, connStatus string) {
|
||||
var unreadParts []string
|
||||
// BufferIndex returns the index of a named buffer, or -1.
|
||||
|
||||
func (ui *UI) BufferIndex(name string) int {
|
||||
for i, buf := range ui.buffers {
|
||||
if buf.Unread > 0 {
|
||||
unreadParts = append(unreadParts, fmt.Sprintf("%d:%s(%d)", i, buf.Name, buf.Unread))
|
||||
if buf.Name == name {
|
||||
return i
|
||||
}
|
||||
}
|
||||
unread := ""
|
||||
if len(unreadParts) > 0 {
|
||||
unread = " [Act: " + strings.Join(unreadParts, ",") + "]"
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
func (ui *UI) setupMessages() {
|
||||
ui.messages = tview.NewTextView().
|
||||
SetDynamicColors(true).
|
||||
SetScrollable(true).
|
||||
SetWordWrap(true).
|
||||
SetChangedFunc(func() {
|
||||
ui.app.Draw()
|
||||
})
|
||||
ui.messages.SetBorder(false)
|
||||
}
|
||||
|
||||
func (ui *UI) setupStatusBar() {
|
||||
ui.statusBar = tview.NewTextView().
|
||||
SetDynamicColors(true)
|
||||
ui.statusBar.SetBackgroundColor(tcell.ColorNavy)
|
||||
ui.statusBar.SetTextColor(tcell.ColorWhite)
|
||||
}
|
||||
|
||||
func (ui *UI) setupInput() {
|
||||
ui.input = tview.NewInputField().
|
||||
SetFieldBackgroundColor(tcell.ColorBlack).
|
||||
SetFieldTextColor(tcell.ColorWhite)
|
||||
|
||||
ui.input.SetDoneFunc(func(key tcell.Key) {
|
||||
if key != tcell.KeyEnter {
|
||||
return
|
||||
}
|
||||
|
||||
text := ui.input.GetText()
|
||||
if text == "" {
|
||||
return
|
||||
}
|
||||
|
||||
ui.input.SetText("")
|
||||
|
||||
if ui.onInput != nil {
|
||||
ui.onInput(text)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (ui *UI) setupKeybindings() {
|
||||
ui.app.SetInputCapture(
|
||||
func(event *tcell.EventKey) *tcell.EventKey {
|
||||
if event.Modifiers()&tcell.ModAlt == 0 {
|
||||
return event
|
||||
}
|
||||
|
||||
r := event.Rune()
|
||||
if r >= '0' && r <= '9' {
|
||||
idx := int(r - '0')
|
||||
ui.SwitchBuffer(idx)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return event
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (ui *UI) setupLayout() {
|
||||
ui.layout = tview.NewFlex().
|
||||
SetDirection(tview.FlexRow).
|
||||
AddItem(ui.messages, 0, 1, false).
|
||||
AddItem(ui.statusBar, 1, 0, false).
|
||||
AddItem(ui.input, 1, 0, true)
|
||||
|
||||
ui.app.SetRoot(ui.layout, true)
|
||||
ui.app.SetFocus(ui.input)
|
||||
}
|
||||
|
||||
// if needed.
|
||||
|
||||
func (ui *UI) refreshStatus() {
|
||||
// Rebuilt from app state by parent QueueUpdateDraw.
|
||||
}
|
||||
|
||||
func (ui *UI) refreshStatusWith(
|
||||
nick, target, connStatus string,
|
||||
) {
|
||||
var unreadParts []string
|
||||
|
||||
for i, buf := range ui.buffers {
|
||||
if buf.Unread > 0 {
|
||||
unreadParts = append(
|
||||
unreadParts,
|
||||
fmt.Sprintf(
|
||||
"%d:%s(%d)", i, buf.Name, buf.Unread,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
bufInfo := fmt.Sprintf("[%d:%s]", ui.currentBuffer, ui.buffers[ui.currentBuffer].Name)
|
||||
unread := ""
|
||||
if len(unreadParts) > 0 {
|
||||
unread = " [Act: " +
|
||||
strings.Join(unreadParts, ",") + "]"
|
||||
}
|
||||
|
||||
bufInfo := fmt.Sprintf(
|
||||
"[%d:%s]",
|
||||
ui.currentBuffer,
|
||||
ui.buffers[ui.currentBuffer].Name,
|
||||
)
|
||||
|
||||
ui.statusBar.Clear()
|
||||
fmt.Fprintf(ui.statusBar, " [%s] %s %s %s%s",
|
||||
connStatus, nick, bufInfo, target, unread)
|
||||
|
||||
_, _ = fmt.Fprintf(
|
||||
ui.statusBar, " [%s] %s %s %s%s",
|
||||
connStatus, nick, bufInfo, target, unread,
|
||||
)
|
||||
}
|
||||
|
||||
func (ui *UI) getOrCreateBuffer(name string) *Buffer {
|
||||
@@ -212,22 +298,9 @@ func (ui *UI) getOrCreateBuffer(name string) *Buffer {
|
||||
return buf
|
||||
}
|
||||
}
|
||||
|
||||
buf := &Buffer{Name: name}
|
||||
ui.buffers = append(ui.buffers, buf)
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
// BufferCount returns the number of buffers.
|
||||
func (ui *UI) BufferCount() int {
|
||||
return len(ui.buffers)
|
||||
}
|
||||
|
||||
// BufferIndex returns the index of a named buffer, or -1.
|
||||
func (ui *UI) BufferIndex(name string) int {
|
||||
for i, buf := range ui.buffers {
|
||||
if buf.Name == name {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user