refactor(vt): track OSC 9;4 state without callbacks; document status bar review nits
- commit
- 467a998f90a75b5cf4d59477ff5159afa07174d9
- author
- brice · · 2026-07-05 08:03 UTC
- parent
- 652325c
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
3 files · +21 -4
M internal/usecase/daemon/status.go +4 -1
| @@ -124,7 +124,8 @@ type mruSession struct { | |||
| 124 | 124 | name string | |
| 125 | 125 | ephemeral bool | |
| 126 | 126 | attention bool | |
| 127 | - | mruAt uint64 | |
| 127 | + | // mruAt orders entries in barStateFor (freshest first); drawing ignores it. | |
| 128 | + | mruAt uint64 | |
| 128 | 129 | } | |
| 129 | 130 | ||
| 130 | 131 | type statusSnapshot struct { | |
| @@ -197,6 +198,8 @@ func (d *Daemon) barStateFor(cur *session, copyFeedback string) barState { | |||
| 197 | 198 | } | |
| 198 | 199 | ||
| 199 | 200 | func fitMRU(entries []mruSession, rowLen, leftUsed int, feedback string) []mruSession { | |
| 201 | + | // With no feedback, keep one blank trailing cell; with feedback, reserve | |
| 202 | + | // its " text" width plus a one-cell gap so drawRightPlainText always fits. | |
| 200 | 203 | copyReserve := 1 | |
| 201 | 204 | if feedback != "" { | |
| 202 | 205 | copyReserve = len([]rune(feedback)) + 2 | |
| @@ -124,7 +124,8 @@ type mruSession struct { | |||||
| 124 | name string | 124 | name string | ||
| 125 | ephemeral bool | 125 | ephemeral bool | ||
| 126 | attention bool | 126 | attention bool | ||
| 127 | - | mruAt uint64 | 127 | + | // mruAt orders entries in barStateFor (freshest first); drawing ignores it. |
| 128 | + | mruAt uint64 | |||
| 128 | } | 129 | } | ||
| 129 | 130 | ||||
| 130 | type statusSnapshot struct { | 131 | type statusSnapshot struct { | ||
| @@ -197,6 +198,8 @@ func (d *Daemon) barStateFor(cur *session, copyFeedback string) barState { | |||||
| 197 | } | 198 | } | ||
| 198 | 199 | ||||
| 199 | func fitMRU(entries []mruSession, rowLen, leftUsed int, feedback string) []mruSession { | 200 | func fitMRU(entries []mruSession, rowLen, leftUsed int, feedback string) []mruSession { | ||
| 201 | + | // With no feedback, keep one blank trailing cell; with feedback, reserve | |||
| 202 | + | // its " text" width plus a one-cell gap so drawRightPlainText always fits. | |||
| 200 | copyReserve := 1 | 203 | copyReserve := 1 | ||
| 201 | if feedback != "" { | 204 | if feedback != "" { | ||
| 202 | copyReserve = len([]rune(feedback)) + 2 | 205 | copyReserve = len([]rune(feedback)) + 2 | ||
M pkg/vt/screen.go +0 -3
| @@ -656,9 +656,6 @@ func (s *Screen) handleOSC(payload []byte) { | |||
| 656 | 656 | s.handleOSC52(string(payload[len("52;"):])) | |
| 657 | 657 | return | |
| 658 | 658 | } | |
| 659 | - | if s.OnNotify == nil && s.OnProgress == nil { | |
| 660 | - | return | |
| 661 | - | } | |
| 662 | 659 | p := string(payload) | |
| 663 | 660 | if p == "9;4" || strings.HasPrefix(p, "9;4;") { | |
| 664 | 661 | s.handleProgress(p[len("9;4"):]) | |
| @@ -656,9 +656,6 @@ func (s *Screen) handleOSC(payload []byte) { | |||||
| 656 | s.handleOSC52(string(payload[len("52;"):])) | 656 | s.handleOSC52(string(payload[len("52;"):])) | ||
| 657 | return | 657 | return | ||
| 658 | } | 658 | } | ||
| 659 | - | if s.OnNotify == nil && s.OnProgress == nil { | |||
| 660 | - | return | |||
| 661 | - | } | |||
| 662 | p := string(payload) | 659 | p := string(payload) | ||
| 663 | if p == "9;4" || strings.HasPrefix(p, "9;4;") { | 660 | if p == "9;4" || strings.HasPrefix(p, "9;4;") { | ||
| 664 | s.handleProgress(p[len("9;4"):]) | 661 | s.handleProgress(p[len("9;4"):]) | ||
M pkg/vt/screen_test.go +17 -0
| @@ -206,6 +206,23 @@ func TestOSC9ProgressNilCallbackDoesNotPanic(t *testing.T) { | |||
| 206 | 206 | }) | |
| 207 | 207 | } | |
| 208 | 208 | ||
| 209 | + | func TestOSC9ProgressSTTerminated(t *testing.T) { | |
| 210 | + | s := NewScreen(10, 2) | |
| 211 | + | var got []bool | |
| 212 | + | s.OnProgress = func(errored bool) { got = append(got, errored) } | |
| 213 | + | s.Write([]byte("\x1b]9;4;1;50\x1b\\\x1b]9;4;0;100\x1b\\")) | |
| 214 | + | require.Equal(t, []bool{false}, got) | |
| 215 | + | } | |
| 216 | + | ||
| 217 | + | func TestOSC9ProgressStateTrackedBeforeCallbackSet(t *testing.T) { | |
| 218 | + | s := NewScreen(10, 2) | |
| 219 | + | s.Write([]byte("\x1b]9;4;1;50\x07")) | |
| 220 | + | var got []bool | |
| 221 | + | s.OnProgress = func(errored bool) { got = append(got, errored) } | |
| 222 | + | s.Write([]byte("\x1b]9;4;0;100\x07")) | |
| 223 | + | require.Equal(t, []bool{false}, got) | |
| 224 | + | } | |
| 225 | + | ||
| 209 | 226 | func TestOnNotifyIgnoresOSC9Progress(t *testing.T) { | |
| 210 | 227 | s := NewScreen(10, 2) | |
| 211 | 228 | calls := 0 | |
| @@ -206,6 +206,23 @@ func TestOSC9ProgressNilCallbackDoesNotPanic(t *testing.T) { | |||||
| 206 | }) | 206 | }) | ||
| 207 | } | 207 | } | ||
| 208 | 208 | ||||
| 209 | + | func TestOSC9ProgressSTTerminated(t *testing.T) { | |||
| 210 | + | s := NewScreen(10, 2) | |||
| 211 | + | var got []bool | |||
| 212 | + | s.OnProgress = func(errored bool) { got = append(got, errored) } | |||
| 213 | + | s.Write([]byte("\x1b]9;4;1;50\x1b\\\x1b]9;4;0;100\x1b\\")) | |||
| 214 | + | require.Equal(t, []bool{false}, got) | |||
| 215 | + | } | |||
| 216 | + | ||||
| 217 | + | func TestOSC9ProgressStateTrackedBeforeCallbackSet(t *testing.T) { | |||
| 218 | + | s := NewScreen(10, 2) | |||
| 219 | + | s.Write([]byte("\x1b]9;4;1;50\x07")) | |||
| 220 | + | var got []bool | |||
| 221 | + | s.OnProgress = func(errored bool) { got = append(got, errored) } | |||
| 222 | + | s.Write([]byte("\x1b]9;4;0;100\x07")) | |||
| 223 | + | require.Equal(t, []bool{false}, got) | |||
| 224 | + | } | |||
| 225 | + | ||||
| 209 | func TestOnNotifyIgnoresOSC9Progress(t *testing.T) { | 226 | func TestOnNotifyIgnoresOSC9Progress(t *testing.T) { | ||
| 210 | s := NewScreen(10, 2) | 227 | s := NewScreen(10, 2) | ||
| 211 | calls := 0 | 228 | calls := 0 | ||