fix(palette): preserve batched tab invalidation
- commit
- 123b9fb668b12fe9b2f04a3f1cd925d69ca852dc
- author
- brice · · 2026-07-14 11:36 UTC
- parent
- b16caf0
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
2 files · +20 -1
M internal/usecase/daemon/palette.go +1 -1
| @@ -125,7 +125,7 @@ func (d *Daemon) handlePaletteInput(ac *attachedClient, data []byte) { | |||
| 125 | 125 | ac.overlays.palette.Backspace() | |
| 126 | 126 | changed = true | |
| 127 | 127 | }, | |
| 128 | - | tab: func() { changed = ac.overlays.palette.CompleteSelected() }, | |
| 128 | + | tab: func() { changed = ac.overlays.palette.CompleteSelected() || changed }, | |
| 129 | 129 | up: func() { ac.overlays.palette.Up(); changed = true }, | |
| 130 | 130 | down: func() { ac.overlays.palette.Down(); changed = true }, | |
| 131 | 131 | cancel: func() { cancel = true }, | |
| @@ -125,7 +125,7 @@ func (d *Daemon) handlePaletteInput(ac *attachedClient, data []byte) { | |||||
| 125 | ac.overlays.palette.Backspace() | 125 | ac.overlays.palette.Backspace() | ||
| 126 | changed = true | 126 | changed = true | ||
| 127 | }, | 127 | }, | ||
| 128 | - | tab: func() { changed = ac.overlays.palette.CompleteSelected() }, | 128 | + | tab: func() { changed = ac.overlays.palette.CompleteSelected() || changed }, |
| 129 | up: func() { ac.overlays.palette.Up(); changed = true }, | 129 | up: func() { ac.overlays.palette.Up(); changed = true }, | ||
| 130 | down: func() { ac.overlays.palette.Down(); changed = true }, | 130 | down: func() { ac.overlays.palette.Down(); changed = true }, | ||
| 131 | cancel: func() { cancel = true }, | 131 | cancel: func() { cancel = true }, | ||
M internal/usecase/daemon/palette_test.go +19 -0
| @@ -586,6 +586,25 @@ func TestPaletteTabCompletesSelectedCommandWithoutForwardingToPTY(t *testing.T) | |||
| 586 | 586 | } | |
| 587 | 587 | } | |
| 588 | 588 | ||
| 589 | + | func TestPaletteBatchedTabCompletionPreservesRepaintInvalidation(t *testing.T) { | |
| 590 | + | writes := make(chan []byte, 1) | |
| 591 | + | p, release := newBlockingPTYWithWrites(t, writes) | |
| 592 | + | defer release() | |
| 593 | + | d, sess, ac, sends := newManualSessionWithPTYs(t, p) | |
| 594 | + | ||
| 595 | + | d.handleInput(sess, ac, []byte("\x1b ")) | |
| 596 | + | awaitFrame(t, sends, ports.MsgOutput) // drain palette-open repaint | |
| 597 | + | ||
| 598 | + | d.handleInput(sess, ac, []byte("NX\t\t")) | |
| 599 | + | ac.overlays.paletteMu.Lock() | |
| 600 | + | got := ac.overlays.palette.Query() | |
| 601 | + | ac.overlays.paletteMu.Unlock() | |
| 602 | + | require.Equal(t, "NXT", got) | |
| 603 | + | awaitFrame(t, sends, ports.MsgOutput) | |
| 604 | + | requireNoPaletteFrame(t, sends) | |
| 605 | + | requireNoPTYWrite(t, writes) | |
| 606 | + | } | |
| 607 | + | ||
| 589 | 608 | func requireNoPaletteFrame(t *testing.T, sends <-chan ports.Frame) { | |
| 590 | 609 | t.Helper() | |
| 591 | 610 | select { | |
| @@ -586,6 +586,25 @@ func TestPaletteTabCompletesSelectedCommandWithoutForwardingToPTY(t *testing.T) | |||||
| 586 | } | 586 | } | ||
| 587 | } | 587 | } | ||
| 588 | 588 | ||||
| 589 | + | func TestPaletteBatchedTabCompletionPreservesRepaintInvalidation(t *testing.T) { | |||
| 590 | + | writes := make(chan []byte, 1) | |||
| 591 | + | p, release := newBlockingPTYWithWrites(t, writes) | |||
| 592 | + | defer release() | |||
| 593 | + | d, sess, ac, sends := newManualSessionWithPTYs(t, p) | |||
| 594 | + | ||||
| 595 | + | d.handleInput(sess, ac, []byte("\x1b ")) | |||
| 596 | + | awaitFrame(t, sends, ports.MsgOutput) // drain palette-open repaint | |||
| 597 | + | ||||
| 598 | + | d.handleInput(sess, ac, []byte("NX\t\t")) | |||
| 599 | + | ac.overlays.paletteMu.Lock() | |||
| 600 | + | got := ac.overlays.palette.Query() | |||
| 601 | + | ac.overlays.paletteMu.Unlock() | |||
| 602 | + | require.Equal(t, "NXT", got) | |||
| 603 | + | awaitFrame(t, sends, ports.MsgOutput) | |||
| 604 | + | requireNoPaletteFrame(t, sends) | |||
| 605 | + | requireNoPTYWrite(t, writes) | |||
| 606 | + | } | |||
| 607 | + | ||||
| 589 | func requireNoPaletteFrame(t *testing.T, sends <-chan ports.Frame) { | 608 | func requireNoPaletteFrame(t *testing.T, sends <-chan ports.Frame) { | ||
| 590 | t.Helper() | 609 | t.Helper() | ||
| 591 | select { | 610 | select { | ||