mirror of
https://github.com/patriceckhart/zot.git
synced 2026-06-26 21:36:31 +02:00
interactive: speed up mouse scrolling in vscode
This commit is contained in:
parent
67c7dc91ea
commit
2eab1339aa
4 changed files with 88 additions and 6 deletions
|
|
@ -336,9 +336,16 @@ func (i *Interactive) Run(ctx context.Context) error {
|
|||
}
|
||||
}()
|
||||
|
||||
_, _ = term.Write([]byte(tui.SeqBracketedPasteOn))
|
||||
mouseSeqOn := ""
|
||||
mouseSeqOff := ""
|
||||
if isVSCodeTerminal() {
|
||||
mouseSeqOn = tui.SeqMouseOn
|
||||
mouseSeqOff = tui.SeqMouseOff
|
||||
}
|
||||
|
||||
_, _ = term.Write([]byte(tui.SeqBracketedPasteOn + mouseSeqOn))
|
||||
_, _ = term.Write([]byte(tui.SeqAltScreenOn))
|
||||
defer term.Write([]byte(tui.SeqAltScreenOff + tui.SeqBracketedPasteOff + tui.SeqShowCursor))
|
||||
defer term.Write([]byte(tui.SeqAltScreenOff + mouseSeqOff + tui.SeqBracketedPasteOff + tui.SeqShowCursor))
|
||||
|
||||
// Streaming pacer: drains buffered text deltas at a steady rate
|
||||
// so typewriter feel is identical across providers regardless of
|
||||
|
|
@ -551,6 +558,21 @@ func (i *Interactive) invalidate() {
|
|||
}
|
||||
}
|
||||
|
||||
func isVSCodeTerminal() bool {
|
||||
return strings.EqualFold(os.Getenv("TERM_PROGRAM"), "vscode")
|
||||
}
|
||||
|
||||
func mouseWheelScrollRows() int {
|
||||
// VS Code's integrated terminal emits relatively small wheel
|
||||
// steps compared with Ghostty's native scrolling. Mouse-wheel
|
||||
// events get a bigger delta than keyboard arrows so trackpads and
|
||||
// wheel mice feel responsive without changing Up/Down behaviour.
|
||||
if isVSCodeTerminal() {
|
||||
return 12
|
||||
}
|
||||
return 6
|
||||
}
|
||||
|
||||
// lastCols returns the current terminal width in columns.
|
||||
func (i *Interactive) lastCols() int {
|
||||
cols, _ := i.cfg.Terminal.Size()
|
||||
|
|
@ -1537,6 +1559,14 @@ func (i *Interactive) handleKey(ctx context.Context, k tui.Key) (done bool) {
|
|||
case tui.KeyPageDown:
|
||||
i.scrollBy(-i.chatPage())
|
||||
return false
|
||||
case tui.KeyMouseWheelUp:
|
||||
i.scrollBy(+mouseWheelScrollRows())
|
||||
return false
|
||||
case tui.KeyMouseWheelDown:
|
||||
if i.scrollOffset > 0 {
|
||||
i.scrollBy(-mouseWheelScrollRows())
|
||||
}
|
||||
return false
|
||||
case tui.KeyUp:
|
||||
// Always use up/down for chat scrolling, even when the editor
|
||||
// contains text. This makes keyboard scrolling consistent with
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ const (
|
|||
KeyCtrlW
|
||||
KeyCtrlO
|
||||
KeyPaste
|
||||
KeyMouseWheelUp
|
||||
KeyMouseWheelDown
|
||||
KeyUnknown
|
||||
)
|
||||
|
||||
|
|
@ -222,6 +224,22 @@ func (r *Reader) readCSI() (Key, error) {
|
|||
}
|
||||
|
||||
func (r *Reader) dispatchCSI(params string, final byte) Key {
|
||||
// SGR mouse mode: CSI < button ; x ; y M/m. Wheel events use
|
||||
// button codes 64 (up) and 65 (down). We ignore coordinates for
|
||||
// now; the chat view only needs scroll direction.
|
||||
if strings.HasPrefix(params, "<") && (final == 'M' || final == 'm') {
|
||||
parts := strings.Split(strings.TrimPrefix(params, "<"), ";")
|
||||
if len(parts) >= 1 {
|
||||
switch parts[0] {
|
||||
case "64":
|
||||
return Key{Kind: KeyMouseWheelUp}
|
||||
case "65":
|
||||
return Key{Kind: KeyMouseWheelDown}
|
||||
}
|
||||
}
|
||||
return Key{Kind: KeyUnknown}
|
||||
}
|
||||
|
||||
// Modified arrow keys come in as CSI 1;<mod><final>. Modifier values
|
||||
// we care about: 2=Shift, 3=Alt/Option, 5=Ctrl. We only extract Alt.
|
||||
var alt bool
|
||||
|
|
|
|||
28
internal/tui/input_test.go
Normal file
28
internal/tui/input_test.go
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
package tui
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestReaderParsesSGRMouseWheel(t *testing.T) {
|
||||
cases := []struct {
|
||||
seq string
|
||||
want KeyKind
|
||||
}{
|
||||
{"\x1b[<64;10;20M", KeyMouseWheelUp},
|
||||
{"\x1b[<65;10;20M", KeyMouseWheelDown},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
idx := 0
|
||||
r := NewReader(func() (byte, error) {
|
||||
b := tc.seq[idx]
|
||||
idx++
|
||||
return b, nil
|
||||
})
|
||||
k, err := r.Read()
|
||||
if err != nil {
|
||||
t.Fatalf("Read(%q): %v", tc.seq, err)
|
||||
}
|
||||
if k.Kind != tc.want {
|
||||
t.Fatalf("Read(%q) kind=%v, want %v", tc.seq, k.Kind, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -89,10 +89,16 @@ const (
|
|||
SeqClearLine = "\x1b[2K"
|
||||
SeqBracketedPasteOn = "\x1b[?2004h"
|
||||
SeqBracketedPasteOff = "\x1b[?2004l"
|
||||
SeqAltScreenOn = "\x1b[?1049h"
|
||||
SeqAltScreenOff = "\x1b[?1049l"
|
||||
SeqSynchronizedOn = "\x1b[?2026h"
|
||||
SeqSynchronizedOff = "\x1b[?2026l"
|
||||
// Basic mouse tracking + SGR extended coordinates. Used only
|
||||
// when explicitly enabled by the interactive mode (currently VS
|
||||
// Code terminal) so terminals with good native scrolling, like
|
||||
// Ghostty, are left alone.
|
||||
SeqMouseOn = "\x1b[?1000h\x1b[?1006h"
|
||||
SeqMouseOff = "\x1b[?1000l\x1b[?1006l"
|
||||
SeqAltScreenOn = "\x1b[?1049h"
|
||||
SeqAltScreenOff = "\x1b[?1049l"
|
||||
SeqSynchronizedOn = "\x1b[?2026h"
|
||||
SeqSynchronizedOff = "\x1b[?2026l"
|
||||
)
|
||||
|
||||
// MoveTo moves the cursor to 1-indexed (row, col).
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue