detached 16f5a24

fix(vt): report modern primary device attributes

commit
16f5a24a32bd1fd3f5cfcec8c42c3c7ab5552b12
author
brice · · 2026-08-26 10:43 UTC
parent
6ace063
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
3 files · +8 -5
M query_test.go +3 -3
@@ -13,8 +13,8 @@ func TestScreenQueryResponses(t *testing.T) {
1313 input string
1414 want string
1515 }{
16- {name: "primary DA bare", input: "\x1b[c", want: "\x1b[?6c"},
17- {name: "primary DA with 0", input: "\x1b[0c", want: "\x1b[?6c"},
16+ {name: "primary DA bare", input: "\x1b[c", want: "\x1b[?62;22c"},
17+ {name: "primary DA with 0", input: "\x1b[0c", want: "\x1b[?62;22c"},
1818 {name: "secondary DA", input: "\x1b[>c", want: "\x1b[>0;0;0c"},
1919 {name: "secondary DA with 0", input: "\x1b[>0c", want: "\x1b[>0;0;0c"},
2020 {name: "DSR status report", input: "\x1b[5n", want: "\x1b[0n"},
@@ -35,7 +35,7 @@ func TestScreenQueryResponses(t *testing.T) {
3535 {name: "DECRQM unknown mode", input: "\x1b[?1337$p", want: "\x1b[?1337;0$y"},
3636 {name: "kitty keyboard query unanswered", input: "\x1b[?u", want: ""},
3737 {name: "XTVERSION unanswered", input: "\x1b[>0q", want: ""},
38- {name: "DA split across writes", input: "", want: "\x1b[?6c"},
38+ {name: "DA split across writes", input: "", want: "\x1b[?62;22c"},
3939 }
4040 for _, tc := range cases {
4141 t.Run(tc.name, func(t *testing.T) {
@@ -13,8 +13,8 @@ func TestScreenQueryResponses(t *testing.T) {
13 input string13 input string
14 want string14 want string
15 }{15 }{
16- {name: "primary DA bare", input: "\x1b[c", want: "\x1b[?6c"},16+ {name: "primary DA bare", input: "\x1b[c", want: "\x1b[?62;22c"},
17- {name: "primary DA with 0", input: "\x1b[0c", want: "\x1b[?6c"},17+ {name: "primary DA with 0", input: "\x1b[0c", want: "\x1b[?62;22c"},
18 {name: "secondary DA", input: "\x1b[>c", want: "\x1b[>0;0;0c"},18 {name: "secondary DA", input: "\x1b[>c", want: "\x1b[>0;0;0c"},
19 {name: "secondary DA with 0", input: "\x1b[>0c", want: "\x1b[>0;0;0c"},19 {name: "secondary DA with 0", input: "\x1b[>0c", want: "\x1b[>0;0;0c"},
20 {name: "DSR status report", input: "\x1b[5n", want: "\x1b[0n"},20 {name: "DSR status report", input: "\x1b[5n", want: "\x1b[0n"},
@@ -35,7 +35,7 @@ func TestScreenQueryResponses(t *testing.T) {
35 {name: "DECRQM unknown mode", input: "\x1b[?1337$p", want: "\x1b[?1337;0$y"},35 {name: "DECRQM unknown mode", input: "\x1b[?1337$p", want: "\x1b[?1337;0$y"},
36 {name: "kitty keyboard query unanswered", input: "\x1b[?u", want: ""},36 {name: "kitty keyboard query unanswered", input: "\x1b[?u", want: ""},
37 {name: "XTVERSION unanswered", input: "\x1b[>0q", want: ""},37 {name: "XTVERSION unanswered", input: "\x1b[>0q", want: ""},
38- {name: "DA split across writes", input: "", want: "\x1b[?6c"},38+ {name: "DA split across writes", input: "", want: "\x1b[?62;22c"},
39 }39 }
40 for _, tc := range cases {40 for _, tc := range cases {
41 t.Run(tc.name, func(t *testing.T) {41 t.Run(tc.name, func(t *testing.T) {
M screen_csi.go +4 -1
@@ -31,7 +31,10 @@ func (s *Screen) applyCSI(params string, cmd byte) {
3131 case 'c':
3232 switch params {
3333 case "", "0":
34- s.respond([]byte("\x1b[?6c"))
34+ // Identify as a VT220-compatible terminal with ANSI color. Modern
35+ // capability probes require a DA1 response with a conformance level;
36+ // the shorter VT102 response is ignored by kitten icat.
37+ s.respond([]byte("\x1b[?62;22c"))
3538 case ">", ">0":
3639 s.respond([]byte("\x1b[>0;0;0c"))
3740 }
@@ -31,7 +31,10 @@ func (s *Screen) applyCSI(params string, cmd byte) {
31 case 'c':31 case 'c':
32 switch params {32 switch params {
33 case "", "0":33 case "", "0":
34- s.respond([]byte("\x1b[?6c"))34+ // Identify as a VT220-compatible terminal with ANSI color. Modern
35+ // capability probes require a DA1 response with a conformance level;
36+ // the shorter VT102 response is ignored by kitten icat.
37+ s.respond([]byte("\x1b[?62;22c"))
35 case ">", ">0":38 case ">", ">0":
36 s.respond([]byte("\x1b[>0;0;0c"))39 s.respond([]byte("\x1b[>0;0;0c"))
37 }40 }
M screen_graphics_test.go +1 -1
@@ -56,7 +56,7 @@ func TestScreenKittyIcatDetectionResponses(t *testing.T) {
5656 "\x1b_Gi=1;OK\x1b\\",
5757 "\x1b_Gi=2;ENOTSUP\x1b\\",
5858 "\x1b_Gi=3;ENOTSUP\x1b\\",
59- "\x1b[?6c",
59+ "\x1b[?62;22c",
6060 }, responses)
6161 }
6262
@@ -56,7 +56,7 @@ func TestScreenKittyIcatDetectionResponses(t *testing.T) {
56 "\x1b_Gi=1;OK\x1b\\",56 "\x1b_Gi=1;OK\x1b\\",
57 "\x1b_Gi=2;ENOTSUP\x1b\\",57 "\x1b_Gi=2;ENOTSUP\x1b\\",
58 "\x1b_Gi=3;ENOTSUP\x1b\\",58 "\x1b_Gi=3;ENOTSUP\x1b\\",
59- "\x1b[?6c",59+ "\x1b[?62;22c",
60 }, responses)60 }, responses)
61 }61 }
62 62

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 2

Tags 13

↑↓ moveenter openesc close