fix: golangci-lint v2 config and lint-clean production code

- Fix .golangci.yml for v2 format (linters-settings -> linters.settings)
- All production code now passes golangci-lint with zero issues
- Line length 88, funlen 80/50, cyclop 15, dupl 100
- Extract shared helpers in db (scanChannels, scanInt64s, scanMessages)
- Split runMigrations into applyMigration/execMigration
- Fix fanOut return signature (remove unused int64)
- Add fanOutSilent helper to avoid dogsled
- Rewrite CLI code for lint compliance (nlreturn, wsl_v5, noctx, etc)
- Rename CLI api package to chatapi to avoid revive var-naming
- Fix all noinlineerr, mnd, perfsprint, funcorder issues
- Fix db tests: extract helpers, add t.Parallel, proper error checks
- Broker tests already clean
- Handler integration tests still have lint issues (next commit)
This commit is contained in:
clawbot
2026-02-10 18:50:24 -08:00
committed by user
parent d6408b2853
commit a7792168a1
16 changed files with 2404 additions and 980 deletions

View File

@@ -31,7 +31,6 @@ type UI struct {
}
// NewUI creates the tview-based IRC-like UI.
func NewUI() *UI {
ui := &UI{
app: tview.NewApplication(),
@@ -40,67 +39,66 @@ func NewUI() *UI {
},
}
ui.setupMessages()
ui.setupStatusBar()
ui.setupInput()
ui.setupKeybindings()
ui.setupLayout()
ui.initMessages()
ui.initStatusBar()
ui.initInput()
ui.initKeyCapture()
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)
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) {
func (ui *UI) AddLine(bufferName, line string) {
ui.app.QueueUpdateDraw(func() {
buf := ui.getOrCreateBuffer(bufferName)
buf.Lines = append(buf.Lines, line)
// Mark unread if not currently viewing this buffer.
if ui.buffers[ui.currentBuffer] != buf {
cur := ui.buffers[ui.currentBuffer]
if cur != buf {
buf.Unread++
ui.refreshStatus()
ui.refreshStatusBar()
}
// If viewing this buffer, append to display.
if ui.buffers[ui.currentBuffer] == buf {
if cur == buf {
_, _ = fmt.Fprintln(ui.messages, line)
}
})
}
// AddStatus adds a line to the status buffer (buffer 0).
// AddStatus adds a line to the status buffer.
func (ui *UI) AddStatus(line string) {
ts := time.Now().Format("15:04")
ui.AddLine(
"(status)",
fmt.Sprintf("[gray]%s[white] %s", ts, line),
"[gray]"+ts+"[white] "+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) {
@@ -119,12 +117,12 @@ func (ui *UI) SwitchBuffer(n int) {
}
ui.messages.ScrollToEnd()
ui.refreshStatus()
ui.refreshStatusBar()
})
}
// SwitchToBuffer switches to the named buffer, creating it
// SwitchToBuffer switches to named buffer, creating if
// needed.
func (ui *UI) SwitchToBuffer(name string) {
ui.app.QueueUpdateDraw(func() {
buf := ui.getOrCreateBuffer(name)
@@ -146,28 +144,25 @@ func (ui *UI) SwitchToBuffer(name string) {
}
ui.messages.ScrollToEnd()
ui.refreshStatus()
ui.refreshStatusBar()
})
}
// SetStatus updates the status bar text.
func (ui *UI) SetStatus(
nick, target, connStatus string,
) {
ui.app.QueueUpdateDraw(func() {
ui.refreshStatusWith(nick, target, connStatus)
ui.renderStatusBar(nick, target, connStatus)
})
}
// 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.
// BufferIndex returns the index of a named buffer.
func (ui *UI) BufferIndex(name string) int {
for i, buf := range ui.buffers {
if buf.Name == name {
@@ -178,7 +173,7 @@ func (ui *UI) BufferIndex(name string) int {
return -1
}
func (ui *UI) setupMessages() {
func (ui *UI) initMessages() {
ui.messages = tview.NewTextView().
SetDynamicColors(true).
SetScrollable(true).
@@ -189,14 +184,14 @@ func (ui *UI) setupMessages() {
ui.messages.SetBorder(false)
}
func (ui *UI) setupStatusBar() {
func (ui *UI) initStatusBar() {
ui.statusBar = tview.NewTextView().
SetDynamicColors(true)
ui.statusBar.SetBackgroundColor(tcell.ColorNavy)
ui.statusBar.SetTextColor(tcell.ColorWhite)
}
func (ui *UI) setupInput() {
func (ui *UI) initInput() {
ui.input = tview.NewInputField().
SetFieldBackgroundColor(tcell.ColorBlack).
SetFieldTextColor(tcell.ColorWhite)
@@ -219,7 +214,7 @@ func (ui *UI) setupInput() {
})
}
func (ui *UI) setupKeybindings() {
func (ui *UI) initKeyCapture() {
ui.app.SetInputCapture(
func(event *tcell.EventKey) *tcell.EventKey {
if event.Modifiers()&tcell.ModAlt == 0 {
@@ -239,34 +234,21 @@ func (ui *UI) setupKeybindings() {
)
}
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)
func (ui *UI) refreshStatusBar() {
// Placeholder; full refresh needs nick/target context.
}
// if needed.
func (ui *UI) refreshStatus() {
// Rebuilt from app state by parent QueueUpdateDraw.
}
func (ui *UI) refreshStatusWith(
func (ui *UI) renderStatusBar(
nick, target, connStatus string,
) {
var unreadParts []string
for i, buf := range ui.buffers {
if buf.Unread > 0 {
unreadParts = append(
unreadParts,
unreadParts = append(unreadParts,
fmt.Sprintf(
"%d:%s(%d)", i, buf.Name, buf.Unread,
"%d:%s(%d)",
i, buf.Name, buf.Unread,
),
)
}
@@ -286,8 +268,8 @@ func (ui *UI) refreshStatusWith(
ui.statusBar.Clear()
_, _ = fmt.Fprintf(
ui.statusBar, " [%s] %s %s %s%s",
_, _ = fmt.Fprintf(ui.statusBar,
" [%s] %s %s %s%s",
connStatus, nick, bufInfo, target, unread,
)
}