diff --git a/main.go b/main.go index 5459089..334b59b 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "github.com/gdamore/tcell/v2" "github.com/rivo/tview" ) @@ -19,9 +20,23 @@ var ( toolCollapsed = true statusLineTempl = "help (F12) | chat: [orange:-:b]%s[-:-:-] (F1) | [%s:-:b]tool use[-:-:-] (ctrl+k) | model: [%s:-:b]%s[-:-:-] (ctrl+l) | [%s:-:b]skip LLM resp[-:-:-] (F10) | API: [orange:-:b]%s[-:-:-] (ctrl+v)\nwriting as: [orange:-:b]%s[-:-:-] (ctrl+q) | bot will write as [orange:-:b]%s[-:-:-] (ctrl+x)" focusSwitcher = map[tview.Primitive]tview.Primitive{} + app *tview.Application ) func main() { + app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { + if event.Key() == tcell.KeyCtrlC { + logger.Info("caught Ctrl+C via tcell event") + go func() { + if err := pwShutDown(); err != nil { + logger.Error("shutdown failed", "err", err) + } + app.Stop() + }() + return nil // swallow the event + } + return event + }) pages.AddPage("main", flex, true, true) if err := app.SetRoot(pages, true).EnableMouse(cfg.EnableMouse).EnablePaste(true).Run(); err != nil { diff --git a/tools_playwright.go b/tools_playwright.go index 7920230..3555469 100644 --- a/tools_playwright.go +++ b/tools_playwright.go @@ -101,6 +101,14 @@ var ( page playwright.Page ) +func pwShutDown() error { + if pw == nil { + return nil + } + pwStop(nil) + return pw.Stop() +} + func installPW() error { err := playwright.Install(&playwright.RunOptions{Verbose: false}) if err != nil { diff --git a/tui.go b/tui.go index 6920db2..4a8a7b2 100644 --- a/tui.go +++ b/tui.go @@ -10,6 +10,7 @@ import ( "path" "strconv" "strings" + "time" "github.com/gdamore/tcell/v2" "github.com/rivo/tview" @@ -21,7 +22,6 @@ func isFullScreenPageActive() bool { } var ( - app *tview.Application pages *tview.Pages textArea *tview.TextArea editArea *tview.TextArea @@ -137,6 +137,42 @@ func setShellMode(enabled bool) { }() } +// showToast displays a temporary message in the top‑right corner. +// It auto‑hides after 3 seconds and disappears when clicked. +func showToast(title, message string) { + // Create a small, bordered text view for the notification. + notification := tview.NewTextView(). + SetTextAlign(tview.AlignCenter). + SetDynamicColors(true). + SetRegions(true). + SetText(fmt.Sprintf("[yellow]%s[-]\n", message)). + SetChangedFunc(func() { + app.Draw() + }) + notification.SetTitleAlign(tview.AlignLeft). + SetBorder(true). + SetTitle(title) + // Wrap it in a full‑screen Flex to position it in the top‑right corner. + // Outer Flex (row) pushes content to the top; inner Flex (column) pushes to the right. + background := tview.NewFlex().SetDirection(tview.FlexRow). + AddItem(nil, 0, 1, false). // top spacer + AddItem(tview.NewFlex().SetDirection(tview.FlexColumn). + AddItem(nil, 0, 1, false). // left spacer + AddItem(notification, 40, 1, true), // notification width 40 + 5, 1, false) // notification height 5 + // Generate a unique page name (e.g., using timestamp) to allow multiple toasts. + pageName := fmt.Sprintf("toast-%d", time.Now().UnixNano()) + pages.AddPage(pageName, background, true, true) + // Auto‑dismiss after 3 seconds. + time.AfterFunc(3*time.Second, func() { + app.QueueUpdateDraw(func() { + if pages.HasPage(pageName) { + pages.RemovePage(pageName) + } + }) + }) +} + func init() { // Start background goroutine to update model color cache startModelColorUpdater() @@ -575,6 +611,7 @@ func init() { if scrollToEndEnabled { status = "enabled" } + showToast("autoscroll", "Auto-scrolling "+status) if err := notifyUser("autoscroll", "Auto-scrolling "+status); err != nil { logger.Error("failed to send notification", "error", err) }