Every native feature is reached through a real nvim mapping, registered
after your config has run so <leader> is whatever you set mapleader
to. :map, maparg(), and which-key see these exactly as they see your
own mappings.
The table below is generated from default_maps() in
crates/view-core/src/native/mappings.rs.
| key | feature | command |
|---|---|---|
<leader>ff |
picker |
:View picker files |
<leader>fb |
picker |
:View picker buffers |
<leader>fg |
picker |
:View picker grep |
<leader>e |
tree |
:View tree toggle |
<leader>fm |
notifications |
:View notifications history |
<leader>fp |
notifications |
:View notifications pause |
<leader>ai |
ai |
:View ai toggle |
<leader><leader> |
palette |
:View palette open |
<leader>ug |
ui |
:View ui gaps |
<leader>uw |
ui |
:View ui cycle_surfaces |
<leader>wn |
window |
:View window new |
<leader>wz |
window |
:View window zoom |
<leader>wf |
window |
:View window fit |
<leader>ws |
window |
:View window flip |
<leader>uf |
window |
:View window float |
<C-w>m |
window |
:View window resize_mode |
<leader>fd |
notifications |
:View notifications dismiss |
<leader>w1 |
window |
:View window to_tabpage_1 |
<leader>w2 |
window |
:View window to_tabpage_2 |
<leader>w3 |
window |
:View window to_tabpage_3 |
<leader>w4 |
window |
:View window to_tabpage_4 |
<leader>w5 |
window |
:View window to_tabpage_5 |
<leader>w6 |
window |
:View window to_tabpage_6 |
<leader>w7 |
window |
:View window to_tabpage_7 |
<leader>w8 |
window |
:View window to_tabpage_8 |
<leader>w9 |
window |
:View window to_tabpage_9 |
<leader>fk |
keys |
:View keys log |
<leader>fv |
dvr |
:View dvr scrub |
<leader>fp holds the notices on screen: none leaves on its own until you
press it again, and the top notice carries a mark while they are held. New
notices still arrive, and <leader>fd still takes the newest one down.
The AI panel is non-modal: <Esc> steps out of it and leaves it on screen
beside your buffer. So <leader>ai (:View ai toggle) is one verb over the
three states that gives you:
| the panel is | <leader>ai |
|---|---|
| closed | opens it and puts the cursor in the composer |
| open, and you are in it | closes it |
| open, and you are not in it | puts you back in it |
A panel you are not in, one you escaped out of or one an agent's own permission request opened beside you, is one press from being yours. Closing it, whichever way you get there, leaves the agent session running, and reopening it brings the transcript back where you left it.
With placement = "windowed" under [ui.surfaces.tree] the file tree takes
a window of its own beside your buffers, and <leader>e reads it over the
same three states:
| the tree is | <leader>e |
|---|---|
| closed | opens it and puts the cursor in it |
| open, and you are in it | closes it |
| open, and you are not in it | puts you back in it |
Close the tree while its window is the only window of the only tab and the window stays, holding the file you were last in. On any other tab the window closes, and the tab goes with it.
Open a file in the tree's window and the window is yours from that moment:
the file is what you read there, with the look your config gives that file,
and the next <leader>e opens a fresh tree beside it.
<Esc> inside the tree takes you back to the window you came from and
leaves the tree standing. Under the default placement = "overlay" the
tree draws over your buffers instead, and <Esc> closes it.
Inside the tree:
| key | does |
|---|---|
<Down>, <Up> |
move the selection |
<CR> on a directory |
opens or closes it |
<CR> on a file |
opens the file; a floating tree closes, and a windowed tree stays while the file opens in the window you came from |
a |
asks for a name and creates that file in the selected directory, beside the selected file, or at the tree's root when nothing is selected |
r |
asks for a new name for the selected file |
d |
asks before it deletes the selected file |
<Esc> |
as above |
r and d do nothing on a directory. The resize keys under
Resizing the sidebars work here too.
<leader>ff, <leader>fb and <leader>fg open the picker on your files,
your buffers or a search of your files' text. A key that types a character
adds it to the query.
| key | does |
|---|---|
<Down> |
select the next result |
<C-n> |
select the next result |
<C-j> |
select the next result |
<Up> |
select the previous result |
<C-p> |
select the previous result |
<C-k> |
select the previous result |
<CR> |
open the selected result in the window you came from |
<C-v> |
open the selected result in a vertical split |
<C-x> |
open the selected result in a horizontal split |
<C-t> |
open the selected result in a new tab |
<BS> |
delete the last character of the query |
<Esc> |
close the picker |
A file opens the file, a search result opens its file on the matching line,
and a buffer switches to that buffer. The picker closes as the result opens.
With no results, <CR> does nothing.
Every key view does not own itself is forwarded to nvim by the name nvim gives it, which is the name your mapping is written in. That includes the four chords a terminal spells as bare control bytes:
| you press | the terminal sends | view forwards | stock vim |
|---|---|---|---|
Ctrl+\ |
0x1c |
<C-\> |
<C-\><C-n>, the way out of terminal and insert mode |
Ctrl+] |
0x1d |
<C-]> |
jump to the tag under the cursor |
Ctrl+^ |
0x1e |
<C-^> |
edit the alternate file |
Ctrl+/ |
0x1f |
<C-_> |
what most comment plugins map |
On a terminal that speaks the kitty keyboard protocol the same chords
arrive as key reports, and Ctrl+4 then arrives as the digit key it
says it is, distinct from Ctrl+\. Either way the name view forwards is
the one nvim's own input layer would have produced, so a mapping fires in
view exactly where it fires in nvim:
:nnoremap <C-]> <Cmd>lua vim.lsp.buf.definition()<CR>view answers the omarchy desktop's own chords on a machine with no desktop of its own, and nvim's leader keys on a machine that has one:
[keys]
profile = "auto" # "auto" | "desktop" | "editor"
desktop_modifier = "auto" # "auto" | "super" | "alt""auto" reads the environment, first match wins:
| marker | value |
|---|---|
SSH_CONNECTION set |
desktop |
SSH_TTY set |
desktop |
WAYLAND_DISPLAY set |
editor |
DISPLAY set |
editor |
| macOS, no ssh marker | editor |
| Windows, no ssh marker | editor |
| anything else | desktop |
:View keys profile reports the profile and the modifier, each with the
marker that decided it. :View keys profile desktop|editor|auto flips the
profile for the running session: every key the new profile takes from a
mapping of your own is reported the way the picker example above reports
one, and every key the old profile had taken is given back.
The chords are Super chords, and a terminal reports Super only under
the kitty keyboard protocol. desktop_modifier = "auto" answers super
where the protocol is in force and alt everywhere else; alt reaches
view from every terminal, as the Esc-prefixed spelling every terminal
sends for it.
From a macOS client over ssh, desktop_modifier = "alt" together with the
terminal's Option-as-Alt setting is the pair to use: Terminal, iTerm and
kitty on macOS hold Cmd for themselves before the pty sees it.
47 chords, one row per [keys.desktop] key. super and alt are the two
spellings desktop_modifier picks between; twin is the key that reaches
the same result under the editor profile, and stays bound under both:
| omarchy chord | [keys.desktop] row |
super | alt | reaches | twin |
|---|---|---|---|---|---|
SUPER + LEFT |
focus_left |
<D-Left> |
<M-Left> |
<C-w>h |
<C-w>h |
SUPER + RIGHT |
focus_right |
<D-Right> |
<M-Right> |
<C-w>l |
<C-w>l |
SUPER + UP |
focus_up |
<D-Up> |
<M-Up> |
<C-w>k |
<C-w>k |
SUPER + DOWN |
focus_down |
<D-Down> |
<M-Down> |
<C-w>j |
<C-w>j |
SUPER + SHIFT + LEFT |
move_left |
<S-D-Left> |
<S-M-Left> |
<C-w>H |
<C-w>H |
SUPER + SHIFT + RIGHT |
move_right |
<S-D-Right> |
<S-M-Right> |
<C-w>L |
<C-w>L |
SUPER + SHIFT + UP |
move_up |
<S-D-Up> |
<S-M-Up> |
<C-w>K |
<C-w>K |
SUPER + SHIFT + DOWN |
move_down |
<S-D-Down> |
<S-M-Down> |
<C-w>J |
<C-w>J |
SUPER + W |
close |
<D-w> |
<M-w> |
<C-w>c |
<C-w>c |
SUPER + Q |
close_alt |
<D-q> |
<M-q> |
<C-w>c |
<C-w>c |
SUPER + RETURN |
new_tile |
<D-CR> |
<M-CR> |
:View window new |
<leader>wn |
SUPER + F |
zoom |
<D-f> |
<M-f> |
:View window zoom |
<leader>wz |
SUPER + ALT + F |
full_width |
<M-D-f> |
<C-M-f> |
` | ` |
SUPER + Home |
fit |
<D-Home> |
<M-Home> |
:View window fit |
<leader>wf |
SUPER + J |
flip_split |
<D-j> |
<M-j> |
:View window flip |
<leader>ws |
SUPER + T |
float |
<D-t> |
<M-t> |
:View window float |
<leader>uf |
SUPER + SPACE |
palette |
<D-Space> |
<M-Space> |
:View palette open |
<leader><leader> |
SUPER + ALT + SPACE |
files |
<M-D-Space> |
<C-M-Space> |
:View picker files |
<leader>ff |
SUPER + SHIFT + F |
tree |
<S-D-f> |
<M-F> |
:View tree toggle |
<leader>e |
SUPER + 1 |
tabpage_1 |
<D-1> |
<M-1> |
1gt |
1gt |
SUPER + 2 |
tabpage_2 |
<D-2> |
<M-2> |
2gt |
2gt |
SUPER + 3 |
tabpage_3 |
<D-3> |
<M-3> |
3gt |
3gt |
SUPER + 4 |
tabpage_4 |
<D-4> |
<M-4> |
4gt |
4gt |
SUPER + 5 |
tabpage_5 |
<D-5> |
<M-5> |
5gt |
5gt |
SUPER + 6 |
tabpage_6 |
<D-6> |
<M-6> |
6gt |
6gt |
SUPER + 7 |
tabpage_7 |
<D-7> |
<M-7> |
7gt |
7gt |
SUPER + 8 |
tabpage_8 |
<D-8> |
<M-8> |
8gt |
8gt |
SUPER + 9 |
tabpage_9 |
<D-9> |
<M-9> |
9gt |
9gt |
SUPER + SHIFT + 1 |
to_tabpage_1 |
<S-D-1> |
<M-!> |
:View window to_tabpage_1 |
<leader>w1 |
SUPER + SHIFT + 2 |
to_tabpage_2 |
<S-D-2> |
<M-@> |
:View window to_tabpage_2 |
<leader>w2 |
SUPER + SHIFT + 3 |
to_tabpage_3 |
<S-D-3> |
<M-#> |
:View window to_tabpage_3 |
<leader>w3 |
SUPER + SHIFT + 4 |
to_tabpage_4 |
<S-D-4> |
<M-$> |
:View window to_tabpage_4 |
<leader>w4 |
SUPER + SHIFT + 5 |
to_tabpage_5 |
<S-D-5> |
<M-%> |
:View window to_tabpage_5 |
<leader>w5 |
SUPER + SHIFT + 6 |
to_tabpage_6 |
<S-D-6> |
<M-^> |
:View window to_tabpage_6 |
<leader>w6 |
SUPER + SHIFT + 7 |
to_tabpage_7 |
<S-D-7> |
<M-&> |
:View window to_tabpage_7 |
<leader>w7 |
SUPER + SHIFT + 8 |
to_tabpage_8 |
<S-D-8> |
<M-*> |
:View window to_tabpage_8 |
<leader>w8 |
SUPER + SHIFT + 9 |
to_tabpage_9 |
<S-D-9> |
<M-(> |
:View window to_tabpage_9 |
<leader>w9 |
SUPER + TAB |
tabpage_next |
<D-Tab> |
<M-Tab> |
gt |
gt |
SUPER + SHIFT + TAB |
tabpage_prev |
<S-D-Tab> |
<S-M-Tab> |
gT |
gT |
SUPER + code:20 |
narrower |
<D--> |
<M--> |
<C-w><lt> |
<C-w>< |
SUPER + code:21 |
wider |
<D-=> |
<M-=> |
<C-w>> |
<C-w>> |
SUPER + SHIFT + code:20 |
shorter |
<S-D--> |
<M-_> |
<C-w>- |
<C-w>- |
SUPER + SHIFT + code:21 |
taller |
<S-D-=> |
<M-+> |
<C-w>+ |
<C-w>+ |
SUPER + SHIFT + BACKSPACE |
gaps |
<S-D-BS> |
<C-M-g> |
:View ui gaps |
<leader>ug |
SUPER + SHIFT + ALT + comma |
messages |
<S-M-D-,> |
<C-M-n> |
:View notifications history |
<leader>fm |
SUPER + comma |
dismiss |
<D-,> |
<M-,> |
:View notifications dismiss |
<leader>fd |
SUPER + SHIFT + CTRL + A |
agent |
<C-S-D-a> |
<C-M-a> |
:View ai toggle |
<leader>ai |
The omarchy desktop binds more chords than a terminal session has a use for; each row below names what it would have to reach for:
| omarchy chord | unbound on |
|---|---|
CTRL + ALT + DELETE |
power and session |
SUPER + P |
the Hyprland layout modes |
SUPER + CTRL + F |
compositor surface properties |
SUPER + O |
compositor surface properties |
SUPER + ALT + Home |
the saved window width |
SUPER + L |
the Hyprland layout modes |
SUPER + SHIFT + ALT + code:10 |
workspace moves that skip the switch |
SUPER + SHIFT + ALT + code:11 |
workspace moves that skip the switch |
SUPER + SHIFT + ALT + code:12 |
workspace moves that skip the switch |
SUPER + SHIFT + ALT + code:13 |
workspace moves that skip the switch |
SUPER + SHIFT + ALT + code:14 |
workspace moves that skip the switch |
SUPER + SHIFT + ALT + code:15 |
workspace moves that skip the switch |
SUPER + SHIFT + ALT + code:16 |
workspace moves that skip the switch |
SUPER + SHIFT + ALT + code:17 |
workspace moves that skip the switch |
SUPER + SHIFT + ALT + code:18 |
workspace moves that skip the switch |
SUPER + code:19 |
the tenth workspace has no digit key |
SUPER + SHIFT + code:19 |
the tenth workspace has no digit key |
SUPER + SHIFT + ALT + code:19 |
the tenth workspace has no digit key |
SUPER + S |
the scratchpad |
SUPER + ALT + S |
the scratchpad |
SUPER + grave |
the scratchpad |
SUPER + SHIFT + grave |
the scratchpad |
SUPER + CTRL + TAB |
the Hyprland layout modes |
SUPER + SHIFT + ALT + LEFT |
monitors |
SUPER + SHIFT + ALT + RIGHT |
monitors |
SUPER + SHIFT + ALT + UP |
monitors |
SUPER + SHIFT + ALT + DOWN |
monitors |
ALT + TAB |
ALT + TAB |
ALT + SHIFT + TAB |
ALT + TAB |
CTRL + ALT + TAB |
monitors |
CTRL + ALT + SHIFT + TAB |
monitors |
SUPER + ALT + code:20 |
the fine and coarse resize steps |
SUPER + ALT + code:21 |
the fine and coarse resize steps |
SUPER + SHIFT + ALT + code:20 |
the fine and coarse resize steps |
SUPER + SHIFT + ALT + code:21 |
the fine and coarse resize steps |
SUPER + CTRL + code:20 |
the fine and coarse resize steps |
SUPER + CTRL + code:21 |
the fine and coarse resize steps |
SUPER + CTRL + SHIFT + code:20 |
the fine and coarse resize steps |
SUPER + CTRL + SHIFT + code:21 |
the fine and coarse resize steps |
SUPER + mouse_down |
the Hyprland layout modes |
SUPER + mouse_up |
the Hyprland layout modes |
SUPER + mouse:272 |
compositor surface properties |
SUPER + mouse:273 |
compositor surface properties |
SUPER + G |
window groups |
SUPER + ALT + G |
window groups |
SUPER + ALT + LEFT |
window groups |
SUPER + ALT + RIGHT |
window groups |
SUPER + ALT + UP |
window groups |
SUPER + ALT + DOWN |
window groups |
SUPER + ALT + TAB |
window groups |
SUPER + ALT + SHIFT + TAB |
window groups |
SUPER + CTRL + LEFT |
window groups |
SUPER + CTRL + RIGHT |
window groups |
SUPER + ALT + mouse_down |
window groups |
SUPER + ALT + mouse_up |
window groups |
SUPER + ALT + code:10 |
window groups |
SUPER + ALT + code:11 |
window groups |
SUPER + ALT + code:12 |
window groups |
SUPER + ALT + code:13 |
window groups |
SUPER + ALT + code:14 |
window groups |
SUPER + SLASH |
monitors |
SUPER + ALT + SLASH |
monitors |
SUPER + CTRL + E |
menus |
SUPER + CTRL + C |
capture and media |
SUPER + CTRL + O |
menus |
SUPER + CTRL + H |
menus |
SUPER + SHIFT + code:201 |
menus |
SUPER + ESCAPE |
menus |
XF86PowerOff |
power and session |
SUPER + K |
menus |
SUPER + ALT + K |
menus |
SUPER + CTRL + K |
menus |
SUPER + CTRL + Q |
application launching |
XF86Calculator |
application launching |
SUPER + SHIFT + SPACE |
menus |
SUPER + CTRL + SPACE |
menus |
SUPER + SHIFT + CTRL + SPACE |
menus |
SUPER + BACKSPACE |
compositor surface properties |
SUPER + CTRL + BACKSPACE |
compositor surface properties |
SUPER + CTRL + ALT + F |
compositor surface properties |
SUPER + SHIFT + comma |
notification chords with no verb yet: dismiss all |
SUPER + CTRL + comma |
notification chords with no verb yet: silence |
SUPER + ALT + comma |
notification chords with no verb yet: invoke last |
SUPER + CTRL + I |
power and session |
SUPER + CTRL + N |
compositor surface properties |
SUPER + CTRL + Delete |
monitors |
SUPER + CTRL + ALT + Delete |
monitors |
switch:on:Lid Switch |
power and session |
switch:off:Lid Switch |
power and session |
PRINT |
capture and media |
ALT + PRINT |
capture and media |
SUPER + ALT + code:34 |
capture and media |
SUPER + ALT + code:35 |
capture and media |
SUPER + PRINT |
capture and media |
SUPER + CTRL + PRINT |
capture and media |
SUPER + CTRL + S |
menus |
SUPER + CTRL + PERIOD |
capture and media |
SUPER + CTRL + R |
menus |
SUPER + CTRL + ALT + R |
menus |
SUPER + SHIFT + CTRL + R |
menus |
SUPER + CTRL + ALT + T |
menus |
SUPER + CTRL + ALT + B |
menus |
SUPER + CTRL + ALT + W |
menus |
SUPER + CTRL + A |
menus |
SUPER + CTRL + B |
menus |
SUPER + CTRL + D |
menus |
SUPER + CTRL + ALT + D |
menus |
SUPER + CTRL + W |
menus |
SUPER + CTRL + P |
menus |
SUPER + CTRL + T |
menus |
SUPER + CTRL + Z |
compositor surface properties |
SUPER + CTRL + ALT + Z |
compositor surface properties |
SUPER + CTRL + L |
power and session |
SUPER + SHIFT + RETURN |
application launching |
SUPER + ALT + SHIFT + F |
application launching |
SUPER + SHIFT + B |
application launching |
SUPER + SHIFT + ALT + B |
application launching |
SUPER + SHIFT + N |
application launching |
SUPER + ALT + RETURN |
application launching |
SUPER + CTRL + RETURN |
application launching |
SUPER + SHIFT + M |
application launching |
SUPER + SHIFT + ALT + M |
application launching |
SUPER + SHIFT + D |
application launching |
SUPER + SHIFT + G |
application launching |
SUPER + SHIFT + O |
application launching |
SUPER + SHIFT + W |
application launching |
SUPER + SHIFT + SLASH |
application launching |
SUPER + SHIFT + A |
application launching |
SUPER + SHIFT + ALT + A |
application launching |
SUPER + SHIFT + C |
application launching |
SUPER + SHIFT + E |
application launching |
SUPER + SHIFT + ALT + E |
application launching |
SUPER + SHIFT + Y |
application launching |
SUPER + SHIFT + ALT + G |
application launching |
SUPER + SHIFT + CTRL + G |
application launching |
SUPER + SHIFT + P |
application launching |
SUPER + SHIFT + S |
application launching |
SUPER + SHIFT + X |
application launching |
SUPER + SHIFT + ALT + X |
application launching |
SUPER + CTRL + code:10 |
menus |
SUPER + CTRL + code:11 |
menus |
SUPER + CTRL + code:12 |
menus |
SUPER + CTRL + code:13 |
menus |
SUPER + CTRL + code:14 |
menus |
SUPER + CTRL + code:15 |
menus |
SUPER + CTRL + code:16 |
menus |
SUPER + CTRL + code:17 |
menus |
SUPER + CTRL + code:18 |
menus |
A default key is registered only for a feature that is on, and only for features that are on:
[native]
picker = falseWith that line in view.toml, view registers none of the picker's keys
and whatever your own config mapped <leader>ff to keeps working. The
first time view takes a key you had mapped, it tells you so and names the
line above verbatim.
ai has no [native] entry, since its own enabled state lives in [ai]
instead:
[ai]
enabled = falseWith that line, <leader>ai registers nothing (no [native] line can turn
it off) and :View ai … answers with a notice. The same first-run notice
the picker example above gets applies here too: if <leader>ai was already
yours, taking it is reported, and the line above is what the notice names
to give it back.
<leader>fv is registered only while the session is recorded, which
[dvr] turns on. With [dvr] enabled = false, the default, :View dvr …
answers with a notice saying so.
The desktop chords turn off together, with the profile:
[keys]
profile = "editor"With that line, view registers none of the desktop chords, and every
<C-w>-shaped twin keeps working. A single chord unbinds on its own row,
with an empty value:
[keys.desktop]
close_alt = ""A [keys] or [keys.desktop] entry that moves a key onto one another view
feature holds keeps its default for the run, and view names both features:
[keys]
dvr_scrub = "<leader>ug"view: [keys] dvr_scrub = "<leader>ug" is the key `ui gaps` already holds. `dvr scrub` stays on <leader>fv this run
view --print-caps shows that entry at its default, with the same line
under the rows.
A feature that is off holds no key, so its keys are free to take. The sidebar width keys are held while the file tree, the AI panel or notifications are on. Two entries that swap keys both take effect. When two entries move onto one free key, one keeps it and the other stays on its default, and view names both.
<c-g> and <C-g> are one key, as are <M-x> and <A-x>, <M-CR> and
<M-Enter>, and <leader>fv and <Space>fv when your leader is space.
<C-S-a> and <C-a> are two keys. A key typed in a buffer and a key typed
on one of view's own panels can share a spelling.
view --print-caps runs before your nvim config sets the leader, so it
compares a key written with <leader> only against other keys written with
<leader>. The session checks it against your leader.
While a permission request is up, the entered panel's keys are the digits the prompt paints against its own option rows, in the order the agent offered them:
| key | answers |
|---|---|
1 … 9 |
the option on that row, whatever the agent called it |
<Esc> |
cancels the request |
No letter answers a prompt: the same agent edit that raises the question raises a review in the buffer beside it, and that buffer stays an ordinary editable one. See ai.md for what each option does and for what the two "always" answers stand for.
A proposal is drawn in the file itself, and its keys are buffer-local nvim
mappings on the reviewed buffer, set when the review opens and deleted when
it closes. They are in :map for exactly that window, and they take
nothing from your config in between: the whole set lives under
<leader>h and on ]c/[c. The keys are printed on the header of the
current hunk, in the buffer, where you decide it. The agent panel names the
file and the hunk you are on and prints no keys.
| key | does | command |
|---|---|---|
<leader>ha |
accept the hunk under the cursor | :View review accept |
<leader>hA |
accept every hunk still fresh, as one write | :View review accept_all |
<leader>hx |
reject the hunk under the cursor | :View review reject |
<leader>hR |
re-anchor a hunk your own edit moved under | :View review rediff |
<leader>hq |
leave the review, deciding nothing further | :View review leave |
]c |
the next hunk still awaiting a decision | :View review next |
[c |
the previous hunk still awaiting a decision | :View review prev |
:View review reject_all rejects the whole proposal at once and is the one
verb with no key, so it is asked for by name.
Every verb is also a :View form, which is what to map if you want the
review on keys of your own. A global mapping of yours is never touched, and
a buffer-local one on a key the table above takes (gitsigns puts one on
]c, [c and <leader>hR in every file it attaches to) is given back when
the review ends:
nnoremap <silent> ga <Cmd>View review accept<CR>That form is also the way in when <leader>h is already yours, and the way
out of a review whose buffer view can no longer write to at all.
See ai.md for what a review is and what each decision writes.
While the agent panel has your keys, what you type goes to the composer, and these keys act on the panel:
| key | does |
|---|---|
<CR> |
sends the prompt, once the agent has finished its turn |
<BS> |
deletes the last character of the prompt |
<C-c> |
cancels the agent's turn while it is running |
<PageUp>, <PageDown> |
scroll the transcript a page |
<C-u>, <C-d> |
scroll the transcript half a page |
<C-d> with the crash banner up |
takes the banner down |
<Esc> |
gives your keys back to the editor and leaves the panel open |
A prompt you are writing stays on the composer line while the transcript scrolls.
<CR> in the composer sends the prompt, so a line break is its own key:
| key | does |
|---|---|
<M-CR> |
breaks the line, works everywhere |
<S-CR> |
breaks the line, needs the kitty keyboard protocol |
<CR> |
sends the prompt |
Alt+Enter arrives as ESC + Enter from nearly every terminal, so <M-CR> is
the one to reach for. A shifted Enter is distinguishable from a plain one only
under the kitty keyboard protocol, and where the terminal does not speak it both
send the same byte, so Shift+Enter sends the prompt.
What decides it is the startup capability probe's answer. Every
full-tier terminal answers the kitty keyboard query, which is part of
what defines the tier, and a terminal can answer that query and still land
below full for an unrelated reason, where <S-CR> works too. Over
ssh is the ordinary way to meet that: the tier also wants truecolor, which
takes a second question and a slower round trip, so a kitty-class terminal
can spend the first moments of a session on a lower tier with the keyboard
protocol already on.
| the probe's kitty keyboard answer | what view sends |
|---|---|
| yes | CSI > 1 u once the alternate screen is up, CSI < u before leaving it |
| no | CSI < u on the way out and nothing else. A pop nothing pushed is ignored |
--tier full asserts all three capabilities, so view sends the sequence
without asking first. A terminal that does not speak the protocol ignores
it, and <S-CR> still will not reach the composer.
The window view holds the protocol open for is the one nvim holds it open
for when you run nvim directly in kitty, ghostty or WezTerm. Every exit view
takes for itself pops it before leaving the alternate screen: quitting,
:cq, a panic, an error during startup, and the first
SIGHUP/SIGTERM/SIGINT, which view folds into its own teardown. Four
endings cannot pop it, because no view code runs at all: a second fatal
signal (view's escape hatch for a session that will not die otherwise, which
leaves from the signal handler), SIGQUIT, SIGKILL, and an abort. Those
strand raw mode and the alternate screen too, so reset is the repair; to
put only the keyboard back, printf '\e[<u' pops the protocol on its own.
A pasted line break needs no key at all: paste a multi-line prompt and it keeps its lines.
The break the agent receives is the same \n either way, and the composer
paints the text after it on a row of its own with the cursor on that row.
[keys]
composer_newline = ["<S-CR>", "<M-CR>"] # the defaultsAlt is M- above because that is what view's own encoder emits; A- is
read as the same modifier, so either spelling binds the same key.
The file tree and the AI panel are sidebars, and the focused one resizes with the same keys, 5% of the terminal per press. A windowed notification stream or ticker answers the same keys while it holds focus, stepping columns at a left or right anchor and rows at a top or bottom one:
| key | does |
|---|---|
<S-Right> |
one notch wider |
<C-w>> |
one notch wider |
<S-Left> |
one notch narrower |
<C-w>< |
one notch narrower |
Direction reads the way <C-w>< and <C-w>> do in nvim, right widens and
left narrows, whichever edge the sidebar is pinned to. Two bindings per
direction because macOS Terminal and Termius keep the shifted arrows for
themselves and view never sees them; the chord reaches through both.
<C-w>m enters resize mode on the focused sidebar, and dragging a sidebar's
inner edge with the mouse sets its width; see Resize mode.
These are view's own keys inside its own surfaces, so they take nothing
from your config and appear in no :map listing. They are yours to change:
[keys] takes one key notation per action, or a list of them, and a
binding may be a two-key chord:
[keys]
sidebar_wider = ["<S-Right>", "<C-w>>"] # the defaults
sidebar_narrower = ["<S-Left>", "<C-w><"]The same rules hold for every action [keys] carries, the composer's line
break above included.
Spell a key the way nvim spells it, case and all: <S-Right>. A <...>
notation that cannot be a key is reported: a modifier prefix outside S-,
C-, M- and A-, or a name that is neither a single character nor one
of nvim's own (Left, Right, Up, Down, Home, End, PageUp,
PageDown, CR, Esc, Tab, BS, Del, Insert, Space, lt,
F1...). So is a value view cannot read as keys at all: a value of any
other type, or more than two keys in one binding. Either leaves that one
action on its defaults and says so, and neither ever keeps your config
from loading.
What is still silent is a well-formed name this build never receives: it is accepted and simply never pressed.
A chord's first key waits for exactly one more, and only inside the sidebar
you armed it in. Press something that finishes no binding and it is handled
as if you had pressed it alone, another resize key included, so a doubled
<C-w> is still waiting on the same follower.
A width holds between 15% and 70% and lasts the session. The width a
session starts at is view.toml's:
[native]
tree_width = 25 # percent of the terminal; 15..70, default 30
[ai]
panel_width = 40Both are optional, and neither can fail your config: a whole number outside the range opens at the nearest end, and a fractional or non-numeric value opens at the default and tells you so.
With placement = "windowed" a sidebar's resize keys move nvim's own
window width, the same <C-w>>/<C-w>< command it would answer directly.
The percent it moved to is what the session remembers, so the next
<leader>e or <leader>ai reopens it at that width. Under the default
placement = "overlay" there is no window to resize, so the keys still
step tree_width/panel_width, and a reopened float honours the new
number.
Two windowed surfaces pinned to the same edge are stacked in one nvim column or row. Widening a stacked agent panel and the notification stream beside it keeps the same width, whichever one held focus when you pressed the key. A windowed palette stacked on that edge follows along too, though every keystroke while it is open reaches nvim's own command line.
<C-w>m enters resize mode on the focused window or sidebar. The status
line shows RESIZE where the mode word stands until you leave.
| key | does |
|---|---|
h <Left> |
narrower |
l <Right> |
wider |
j <Down> |
shorter |
k <Up> |
taller |
= |
makes every window the same size (<C-w>=) |
<Esc> <CR> q |
leaves the mode |
A step is 5% of the editor area, at least one cell. A count before the key
multiplies it: 3l is three steps wider. A sidebar steps its share by 5%
along the one direction it has: its width at the left or right, its height
at the top or bottom. Any other key leaves the mode and then does what it
always does.
Rebind the entry key under [keys]:
[keys]
resize_mode = "<C-w>m"Dragging the border between two windows, or a sidebar's edge, with the mouse resizes them too.
| key | does |
|---|---|
<leader>ug |
toggles [ui] gaps for the session |
<leader>uw |
steps every surface through the placement ring |
<leader>ug is a real nvim mapping (:View ui gaps), registered in normal
mode only: insert mode, a windowed surface's own key table and the composer
each bind the same keys first. It works only under panes = "tiles": the
outer grid re-attaches at its new size and every open window is asked for a
fresh inner size, gapped or flush against its neighbours. See
tiled-ui.md for what a gap is.
<leader>uw is :View ui cycle_surfaces, and moves the tree, the agent
panel, the palette and the notification stream together, one ring position
at a time: config (what view.toml gave each of them) -> windowed
-> overlay -> back to config. A surface open when the ring steps
moves with it: the tree keeps its cursor row, the agent panel keeps its
transcript. See tiled-ui.md for what
each position looks like.
Rebind either one under [keys] with a single notation:
[keys]
toggle_gaps = "<leader>ug" # the default
cycle_surfaces = "<leader>uw" # the defaultWhen the editor stops answering keys, a banner offers these:
| key | does |
|---|---|
<C-c> |
interrupts what the editor is doing and keeps it running |
<F5> |
restarts the editor, recovering what its swap files held |
<Esc> |
closes the banner and leaves the editor as it is |
<C-q> |
quits view, offered once the editor's connection has closed |
An error or warning is sticky: it stays on screen until you have read it, where an ordinary message fades on its own. Motions, insert mode and idle time all leave it standing.
<Esc> in the editor takes it down, in any mode:
:bogus
" E492: Not an editor command: bogus -- the toast, still there after 10j
" <Esc> -- goneNothing else changes: the key still reaches nvim exactly as it always did,
so a pending count or operator is cancelled the same way, and <Esc> with
no error showing does nothing new at all. An <Esc> that leaves insert or
visual mode takes the error down with it, and its text stays in the history.
While the banner for an editor that stopped answering is up, <Esc>
answers the banner and the error stays.
Dismissing takes the toast off the screen and keeps the record. Every message view has shown, errors included, stays in the history:
:View notificationswhich <leader>fm also opens.
<Esc> clears nvim's own errors and warnings, and only those. A notice
view raised itself about something it went and checked (a plugin drawing
over the command line, a file that stopped being readable) stays up while
that is still true. Those come down one at a time, with d in the history.
The history overlay lists what view has said this session, newest first, and scrolls:
| key | does |
|---|---|
j |
select the next entry |
k |
select the previous entry |
<C-d> |
select half a screen further down |
<C-u> |
select half a screen further up |
gg |
select the newest entry |
G |
select the oldest entry |
y |
copy the selected entry verbatim, to the system clipboard and over OSC 52 |
d |
take down the standing notice the selected entry belongs to |
<Esc> |
close the history; in a window of its own, go back to the window you came from |
y copies the selected line byte for byte. A path with a space in it arrives
with the space. It goes to your system clipboard and, in the same keystroke, out
as an OSC 52 escape, so a view running over SSH puts the line on the clipboard
of the machine you are reading it on:
:View notifications
" > view: file /home/tj/my notes/plan v2.md is no longer readable
" y -- that line, exactly, on your clipboardIf there is no system clipboard to reach, view says so once and the copy still goes to its own registers and out over OSC 52.
A notice that re-words itself while it stands keeps one entry in the history. The entry carries the newest wording and stays where the first wording put it.
d takes down the notice the selected entry belongs to, wherever the
entry sits in the history. The entry stays after the notice is gone. On a
message from nvim, which has no notice standing behind it, d does
nothing.
<leader>fk opens the key log at the bottom of the screen. It moves to the
top while your cursor is on a line under it, and grows with its rows up to
two fifths of the screen. Each mapping you fire is added to the top as it
runs: the key, whose mapping it is, and for one of view's defaults the
mapping of yours it took the key from.
14:02:11 <Space>fk view keys log
14:02:09 <Space>ff view picker files took it from: Find files (lua/keys.lua:12)
14:02:05 gd buffer Goto definition (lua/lsp.lua:31)
14:02:01 <Space>x yours Close buffer (lua/keys.lua:20)
14:01:58 :View view picker grep
The log keeps your keys going to the buffer while it is open. <leader>fk
again closes it. :View keys focus hands your keys to the log, with the
newest row selected and the cursor left where it was in your buffer.
These keys then work on the log:
| key | does |
|---|---|
j |
select the next older mapping |
k |
select the next newer mapping |
<C-d> |
select half a screen further down |
<C-u> |
select half a screen further up |
gg |
select the newest mapping |
G |
select the oldest mapping |
y |
copy the selected row, to the system clipboard and over OSC 52 |
<Esc> |
close the log |
The log keeps the newest 200 mappings.
A mapping of yours is logged when you fire it in normal mode, in any
window. A mapping set on the current buffer alone, such as one a language
server sets when it attaches, is marked buffer. A mapping whose keys
also begin a longer one is not shown until your next key arrives, and its
row carries the time you pressed its last key. To put the log on another
key:
[keys]
key_log = "<leader>fk" # the defaultWith the session recorded, <leader>fv freezes the screen on the last
frame view drew, and the bottom row becomes the scrub bar:
DVR -1.4s of 42.3s q close h/l frame H/L 1s g/G ends b branch e export
[dvr]
enabled = trueThe bar names how long before the newest frame the one on screen was
drawn, and how far back the recording reaches. When that frame's bottom
row holds a message, the bar moves to the top row. While the scrub is
open, a question the live screen is asking adds ! waiting: q to answer
to the bar, ahead of the keys. These keys move through the frames:
| key | does |
|---|---|
h |
one frame back |
l |
one frame forward |
H |
one second back |
L |
one second forward |
g |
the oldest frame kept |
G |
the newest frame |
q |
back to the live screen |
<Esc> |
back to the live screen |
b |
the editor replaced by one brought to this frame, after a confirm |
e |
the recording written to a clip file, then live |
b starts the editor again from the frame on screen. A fresh editor opens
the files view was started with, and every key, paste, click and resize
recorded before that frame is played into it. The confirm names the
buffers with unsaved changes, which are lost, and the files changed on
disk since recording began, which the fresh editor reads as they are now.
Replay brings back what you typed. A timer, a language server reply or a
key that waited on timeoutlen can land differently the second time. The
frames after the branch point stay in the recording and cannot be branched
from. b is refused while view reads piped input. Keys you press while the
fresh editor starts are typed after the replay. Review and recording
commands do nothing until the replay has finished. The first one says so,
and you press it again after. If the editor has not said the replay
finished a few seconds after its last key, view says so once and keeps
waiting. The wait ends when the editor answers or is restarted. If the fresh
editor cannot start, view restarts the editor and offers the unsaved text
back from its swap files, and a notice says when view could not keep one. If
view quits during a branch, the next view you start puts the swap files
back. Under --remote the swap files are on the other machine, so the
confirm says the unsaved text is lost if the branch fails.
Once the input log reaches its share of [dvr] max_mb, view says so once,
and frames painted after that point cannot be branched from.
After b, the frame stays on screen while view checks the disk, and the bar
says so. A clip still being written is finished first. Only q and <Esc>
act then, and both return to the live screen with no confirm.
The confirm then opens over the frame, and the bar drops its keys. y
branches. n and <Esc> return to the live screen.
e writes view-dvr-<seconds>.vdvr in the directory view was started in.
:View dvr export PATH writes to PATH on the machine view runs on, and
under --remote that is your own machine. ~ at the start of the path is
your home directory, $NAME and ${NAME} are environment variables, and a
relative path starts from the directory view was started in. On Linux and
macOS a backslash keeps the character after it, so a\ b.vdvr names a file
with a space and \~ names a directory called ~. %, # and < stay as
typed. A path is refused, and nothing is written, when it names an
environment variable that is not set, starts with ~user, or holds ${} or
a ${ with no closing }. On Linux and macOS the clip can be read by you
alone. A clip holds the screen as it was shown and nothing you typed. A
notice names the file once it is written. An existing file is left as it
is, and the notice says so. A clip still being written when view quits gets
up to two seconds to finish. One that does not is removed, and view says so
as it exits.
:View dvr play PATH opens a clip in the scrub, with PATH read as the
export path is. The bar starts with CLIP and the clip's file name. The
frame keys move through the clip, and q returns to the live screen.
Pressing b or e in a clip closes it with a notice. Your recording is
kept while a clip is open. The screen is not recorded until the clip is
closed. A clip holds up to [dvr] max_mb of memory beside your recording.
A clip longer than that is loaded from its newest frame, and its bar says
how many of its oldest frames were left out. Playing a clip closes the one
open, and a play while a clip is still being read is refused with a notice
naming that clip.
Every other key, paste and click is dropped while the scrub is open, so
nothing reaches your buffer. Closing it redraws the live screen. How far
back the frames reach depends on [dvr] max_mb: the oldest frames are
dropped as new ones are kept. To put the scrub on another key:
[keys]
dvr_scrub = "<leader>fv" # the defaultThe command is registered whatever you have turned off, so a feature is always reachable even with no keys at all:
:View picker files
:View picker grep
:View tree toggleIt completes each word against every form this build answers, and a third
word where a form takes one: :View ui panes <Tab> offers auto, nvim
and tiles.
Forms with no default key:
| form | does |
|---|---|
:View ai open |
opens the agent panel and puts your keys in it |
:View ai focus |
the same as :View ai open |
:View ai close |
closes the agent panel; the agent session keeps running |
:View ai dismiss |
takes down the agent's crash banner |
:View ui panes |
says which panes look is on, and whose the top row is |
:View ui panes tiles |
gives every window a frame of its own |
:View ui panes nvim |
keeps the shape nvim draws, with its separator column |
:View ui panes auto |
uses the look view picks for this terminal, as panes = "auto" does |