From 5b8880ebc80e3ce4ab4bc5118d1ca657e84c8834 Mon Sep 17 00:00:00 2001 From: Grail Finder Date: Wed, 31 Dec 2025 16:18:18 +0300 Subject: Feat: scroll to end toggle --- bot.go | 14 +++++++++++--- config.example.toml | 1 + config/config.go | 1 + tui.go | 30 ++++++++++++++++++++++++++---- 4 files changed, 39 insertions(+), 7 deletions(-) diff --git a/bot.go b/bot.go index e8a40a7..b91c6e4 100644 --- a/bot.go +++ b/bot.go @@ -674,7 +674,9 @@ out: case chunk := <-chunkChan: fmt.Fprint(tv, chunk) respText.WriteString(chunk) - tv.ScrollToEnd() + if scrollToEndEnabled { + tv.ScrollToEnd() + } // Send chunk to audio stream handler if cfg.TTS_ENABLED { extra.TTSTextChan <- chunk @@ -682,14 +684,18 @@ out: case toolChunk := <-openAIToolChan: fmt.Fprint(tv, toolChunk) toolResp.WriteString(toolChunk) - tv.ScrollToEnd() + if scrollToEndEnabled { + tv.ScrollToEnd() + } case <-streamDone: // drain any remaining chunks from chunkChan before exiting for len(chunkChan) > 0 { chunk := <-chunkChan fmt.Fprint(tv, chunk) respText.WriteString(chunk) - tv.ScrollToEnd() + if scrollToEndEnabled { + tv.ScrollToEnd() + } // Send chunk to audio stream handler if cfg.TTS_ENABLED { extra.TTSTextChan <- chunk @@ -1130,5 +1136,7 @@ func init() { if cfg.STT_ENABLED { asr = extra.NewSTT(logger, cfg) } + // Initialize scrollToEndEnabled based on config + scrollToEndEnabled = cfg.AutoScrollEnabled go updateModelLists() } diff --git a/config.example.toml b/config.example.toml index 594e4da..2227c5e 100644 --- a/config.example.toml +++ b/config.example.toml @@ -18,6 +18,7 @@ ToolRole = "tool" AssistantRole = "assistant" SysDir = "sysprompts" ChunkLimit = 100000 +AutoScrollEnabled = true # AutoCleanToolCallsFromCtx = false # rag settings RAGBatchSize = 1 diff --git a/config/config.go b/config/config.go index 112986b..ff428c9 100644 --- a/config/config.go +++ b/config/config.go @@ -28,6 +28,7 @@ type Config struct { AssistantRole string `toml:"AssistantRole"` SysDir string `toml:"SysDir"` ChunkLimit uint32 `toml:"ChunkLimit"` + AutoScrollEnabled bool `toml:"AutoScrollEnabled"` WriteNextMsgAs string WriteNextMsgAsCompletionAgent string SkipLLMResp bool diff --git a/tui.go b/tui.go index c9788b7..1fc9ddd 100644 --- a/tui.go +++ b/tui.go @@ -36,6 +36,7 @@ var ( roleEditWindow *tview.InputField fullscreenMode bool positionVisible bool = true + scrollToEndEnabled bool = true // pages historyPage = "historyPage" agentPage = "agentPage" @@ -88,6 +89,7 @@ var ( [yellow]Ctrl+q[white]: cycle through mentioned chars in chat, to pick persona to send next msg as [yellow]Ctrl+x[white]: cycle through mentioned chars in chat, to pick persona to send next msg as (for llm) [yellow]Alt+1[white]: toggle shell mode (execute commands locally) +[yellow]Alt+2[white]: toggle auto-scrolling (for reading while LLM types) [yellow]Alt+3[white]: summarize chat history and start new chat with summary as tool response [yellow]Alt+4[white]: edit msg role [yellow]Alt+5[white]: toggle system and tool messages display @@ -203,7 +205,9 @@ func executeCommandAndDisplay(cmdText string) { cmdParts := parseCommand(cmdText) if len(cmdParts) == 0 { fmt.Fprintf(textView, "\n[red]Error: No command provided[-:-:-]\n") - textView.ScrollToEnd() + if scrollToEndEnabled { + textView.ScrollToEnd() + } colorText() return } @@ -233,7 +237,9 @@ func executeCommandAndDisplay(cmdText string) { } } // Scroll to end and update colors - textView.ScrollToEnd() + if scrollToEndEnabled { + textView.ScrollToEnd() + } colorText() } @@ -767,7 +773,9 @@ func init() { updateStatusLine() textView.SetText(chatToText(cfg.ShowSys)) colorText() - textView.ScrollToEnd() + if scrollToEndEnabled { + textView.ScrollToEnd() + } // init sysmap _, err := initSysCards() if err != nil { @@ -792,6 +800,18 @@ func init() { positionVisible = !positionVisible updateFlexLayout() } + if event.Key() == tcell.KeyRune && event.Rune() == '2' && event.Modifiers()&tcell.ModAlt != 0 { + // toggle auto-scrolling + scrollToEndEnabled = !scrollToEndEnabled + status := "disabled" + if scrollToEndEnabled { + status = "enabled" + } + if err := notifyUser("autoscroll", "Auto-scrolling "+status); err != nil { + logger.Error("failed to send notification", "error", err) + } + updateStatusLine() + } if event.Key() == tcell.KeyF1 { // chatList, err := loadHistoryChats() chatList, err := store.GetChatByChar(cfg.AssistantRole) @@ -1292,7 +1312,9 @@ func init() { fmt.Fprintf(textView, "%s[-:-:b](%d) <%s>: [-:-:-]\n%s\n", nl, len(chatBody.Messages), persona, msgText) textArea.SetText("", true) - textView.ScrollToEnd() + if scrollToEndEnabled { + textView.ScrollToEnd() + } colorText() } go chatRound(msgText, persona, textView, false, false) -- cgit v1.2.3