summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGrail Finder <wohilas@gmail.com>2026-03-04 08:25:53 +0300
committerGrail Finder <wohilas@gmail.com>2026-03-04 08:25:53 +0300
commit58ccd63f4a76a8ee6ee44d30347b9a6b12833ebf (patch)
tree75156f8f08f09d590b295c72b027c64eaff38ecd
parent3611d7eb592eabc5bd6a074a16394d05c4315bfa (diff)
Fix: avoid raw terminal after ctrl+c exit
-rw-r--r--main.go15
-rw-r--r--tools_playwright.go8
-rw-r--r--tui.go39
3 files changed, 61 insertions, 1 deletions
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)
}