← Reference

Keyboard shortcuts

All key bindings, grouped by context.

Full notes from the app

Most shortcuts work only when their pane is focused. Mission Control's app-level shortcuts (⌘N, ⌘K, ⌘1–9, ⌘M, ⌘P, ⌘T, ⌘, and the rest below) are rebindable without a recompile via the top-level [keymap] block in config.toml (see the Configuration section's keymap key); Esc and Ctrl-C stay built-in.

71 entries · kcode 0.4.0

71 shown
KeysContextWhat it does
Global
Ctrl-LGlobalToggle the control drawer (open/close). Works even under an open modal — but while a modal (permission ask, picker) is showing, it owns every other key: a focused drawer pane can't steal the modal's Enter/Esc/arrows.
Chat pane
Ctrl-PChat paneOpen the command palette (actions + config + your own skills and markdown commands, not just slash names).
PageUp / PageDownChat paneScroll the transcript by 12 lines.
Ctrl-↑ / Ctrl-↓Chat paneScroll the transcript by 1 line.
Ctrl-Home / Ctrl-EndChat paneJump to top / bottom of the transcript.
Alt-↑ / Alt-↓Chat paneFocus the previous / next message bubble (the focused one takes a distinct accent focus background; message kinds are told apart by colour, not a gutter bar — your accent-banded message, the model's plain-foreground reply, dim-italic reasoning, code-toned tool rows — see 'Chat text differentiation'). Inert while the palette or a modal is open — the topmost surface owns the keyboard.
Ctrl-Shift-CChat paneCopy the active in-app mouse selection if there is one, otherwise the keyboard-focused bubble (Alt-↑/↓), to the clipboard. Writes the native clipboard (the confirmed path) plus best-effort OSC 52 for SSH/tmux; the 'Copied N chars' flash only fires when the native write actually succeeds.
Chat input
@file:<path>Chat inputAttach an image to the turn: write @file:diagram.png (relative paths resolve against the project root, absolute paths as-is) and the image rides on your message. PNG/JPEG/GIF/WebP/BMP/TIFF/ICO are supported; PDFs are not yet. A model without vision sees a short text note naming the file instead of the image, and an unreadable path surfaces a system notice without blocking the turn.
Mission Control chat input (GPU, maximized)
Paste image (Cmd/Ctrl-V)Mission Control chat input (GPU, maximized)Paste an image from the system clipboard straight onto the turn: Cmd-V / Ctrl-V with an image on the clipboard attaches it instead of pasting text (a text clipboard still pastes text). Both clipboard shapes work: image data (a screenshot or copy-from-browser) and an OS file copy — Cmd-C on one or more image files in Finder, then Cmd-V in the input queues each image file exactly like a drop (non-image files in the copy are ignored, and a copy with no images pastes as usual). The bytes decode off the render frame; pending attachments show as removable chips above the input (2026-08-05 — one chip per image: 'pasted image' or the dropped file's basename, each with an × that removes just that attachment; the earlier bare count line gave no way to take a mistaken paste back). Same formats and non-vision fallback as @file: (PNG/JPEG/GIF/WebP/BMP/TIFF/ICO; PDFs not yet). A paste that can't be decoded surfaces a system notice in the panel and is dropped without blocking the turn.
Drag-and-drop imageMission Control chat input (GPU, maximized)Drop image files from the OS onto the maximized session to attach them to the turn; non-image files are ignored by extension. Each dropped image rides on your message, decoded off the render frame, and the same pending-attachment count line shows how many are queued until Enter. A model without vision sees a short text note instead of the image, and a file that can't be decoded surfaces a system notice in the panel without blocking the turn.
Chat pane (turn streaming)
EscChat pane (turn streaming)Cancel the in-flight turn — but only as the base layer: anything dismissible open above the transcript (palette, picker or permission modal, /config editor, focused drawer pane, bubble focus) consumes Esc first, one layer per press. On a permission prompt, Esc denies the tool call and the turn continues with the denial; press Esc again to cancel the turn itself.
Ctrl-CChat pane (turn streaming)Cancel the in-flight turn unconditionally — works even with a palette or modal open. With no turn active, Ctrl-C (like Ctrl-D) exits the session.
Chat pane (bubble focused)
EscChat pane (bubble focused)Clear focus on the bubble; return to prompt input.
Chat pane (default)
Drag (release)Chat pane (default)With mouse capture on (the default), click-drag to select text in-app; releasing auto-copies with a 'Copied N chars' status flash — no Cmd/Ctrl-C needed. A reverse-video highlight shows the selection; clicking never shifts the bubble. Copies message text only: the right-aligned timestamp is a rendering decoration, excluded from both the copy and the highlight.
Chat pane (GPU)
↓ latest (jump-to-bottom pill)Chat pane (GPU)While you have scrolled up off the bottom of the transcript, a floating '↓ latest' chip appears at the transcript's bottom-right corner (pinned above the input, not scrolling with the messages); click it to jump straight back to the newest content. The down-scroll twin of the '↑ your last message' pill — it hides again the moment you are back at the bottom, and only shows on the live editable session, never on a grid preview miniature (#180). GPU build only.
Click a linkChat pane (GPU)Hyperlinks and file paths in a message render as clickable, accent-toned, underlined links: click one to open it — a http(s) URL in your default browser, a file path in your editor (the same editor resolution as a /files click, session_panels.files.editor). Both markdown [text](url) links and bare tokens found in plain prose are linkified — and so is a whole file path written in backticks (` libs/foo.rs , how the agent usually names one): a backticked path keeps its monospace code-chip look but takes the accent tone + pointer that mark it clickable (no underline — the chip is its affordance), so only a full-span path linkifies and a command like grep foo src/ stays a plain chip. The file name in a Read / Edit / Write tool-call header is clickable the same way — click it to open that file in your editor. Path detection resolves every candidate against the session's project directory and links it only when the file really exists on disk (2026-08-07, amending #179's shape-only rule): a relative token like libs/foo/bar.rs or Cargo.toml resolves against the project root, ~/ expands against your home, absolute paths pass through — and the click target is the resolved absolute path, so the editor opens the right file regardless of cwd. A trailing line number rides along — src/main.rs:42 opens at line 42. A slash-path naming an existing *directory* links too, while a token that resolves to nothing (or prose like e.g. / and/or`) stays plain text. URLs linkify by scheme alone, unchecked. A link opens on press (mouse-down on it) — the same press-to-act idiom every other transcript control uses, so it fires reliably (#187); a selection dragged in from outside the link still crosses it and selects, but a press that begins on a link opens it rather than starting a selection there. A pure client-side action — opening a browser or editor never reaches the daemon. GPU build only.
Palette
↑ / ↓PaletteMove palette selection.
EnterPaletteAccept the highlighted palette entry.
EscPaletteClose the palette without selecting. Leaves a streaming turn untouched.
Slash autocomplete
TabSlash autocompleteCommit the highlighted slash-command match.
↑ / ↓Slash autocompleteMove highlight up/down through matches.
Prompt input
Shift-Enter / Alt-Enter / Ctrl-JPrompt inputInsert a newline; stays in edit mode.
EnterPrompt inputSubmit the prompt.
EscPrompt inputCancel the current input.
Prompt input (empty)
↑ / ↓Prompt input (empty)Navigate prompt history (persisted per project — survives restarts). While a turn is running, further submitted messages park in the session's client-side queue (dimmed rows above the composer, N queued messages · ↑ pulls the newest back) instead of being sent; they flush in order as each turn completes, promoting from dimmed rows to real transcript bubbles — and mid-turn, the agent loop pulls the queue at each round boundary (2026-08-10): parked lines join the in-flight turn as user messages the model sees on its next round, instead of waiting for the turn to end (attachments ride along, decoded exactly like a fresh turn). The pull is lossless (2026-08-14): a parked line leaves the queue and echoes as a bubble only once the daemon confirms it injected that line into the turn — a pull that times out mid round-trip keeps every line parked for the next boundary rather than dropping it. ↑ on an empty composer pops the newest queued message back into the composer as an ordinary draft (attachments included) — edit it and Enter resends it as a fresh submission (the back of the queue while still busy, straight to the agent once idle). With text in the composer, ↑ walks prompt history as usual and never touches the queue. Prompt history records a queued line only when it actually sends. The queue is in-memory and client-side by design — it does not survive closing the window.
Settings dir editor
EnterSettings dir editorSubmit and save the current field (the Advanced-section Command/Skill dirs text editor).
EscSettings dir editorCancel — close without saving.
Mission Control (GPU)
Click a session tileMission Control (GPU)Maximize that session into the live, editable window. Grid tiles are live miniatures — each is the full maximized session view rendered and then uniformly scaled down (one scale factor, so fonts and UI look tiny), a picture-in-picture / Exposé thumbnail, not the view re-laid-out at tile size — which you click to blow up. The click always maximizes immediately — the hover-zoom (peek) never intercepts it; pressing a tile drops any in-flight peek so the click wins. The grid holds only session tiles.
Approach a session tile (hover announce)Mission Control (GPU)The instant the pointer rests on a grid session tile — before the ~1s dwell-peek — the tile announces itself: it solidifies (frost off, fully opaque so its content reads crisply), rises slightly toward you (a partial version of the pick-up lift, expressed as a deeper drop shadow since gpui has no element transform), and gains a soft foreground edge-glow, gentler and tighter than a picked-up tile's halo. This is the middle rung of the rest → approach → peek → click grammar (product §15.3): rest is quiet frosted glass, approach leans in, dwell peeks, click maximizes. Tune the feel with session_presenter.approach.lift_pct / session_presenter.approach.glow_pct, or turn the rise+glow off (keeping the solidify) with session_presenter.approach.enabled. Rendered by Wingman. GPU build only.
Peek a session tile (hover-dwell)Mission Control (GPU)Rest the pointer on a grid session tile for ~1 second and it floats to ~75% over a blurred, dimmed grid — a transient read of that session without committing to it (dwell peeks, click maximizes). The float is the same live miniature, just enlarged and sharp; click the enlarged card to maximize that session into Focus; slide the pointer off both the card and its cell and it settles straight back to the grid — no mode change. A pointer over the floating card holds the peek open, so reaching across to click it never settles it out. A click always wins over the peek: pressing any tile immediately drops the hover-zoom (and none forms while the button is held), so a click never lands mid-float — it maximizes the tile you pressed, and pressing the enlarged card maximizes its session in any phase of the float. Tune or disable it with session_presenter.peek.enabled / session_presenter.peek.dwell_ms. Rendered by Wingman; which tiles may peek is kcode's call. GPU build only.
Empty grid (invitation)Mission Control (GPU)When no sessions are on the grid, a calm centred invitation — 'right-click to begin · ⌘N' — fills the empty grid as a full-window overlay carrying the same deep control-room gradient as a populated grid (so an empty grid reads as the same dark room, not flat slate), teaching the two ways to start a session. The prompt softly breathes (a gentle opacity pulse on Wingman's Breathe motion token; static when animations are off — only the prompt breathes, the gradient stays still) and vanishes the instant a session exists. It is purely visual — a right-click or ⌘N passes straight through it to summon the context menu / new-session directory picker. There is no '+' launcher tile: spawning is via ⌘N, the context menu's 'New session', and the palette's 'New session'.
Single-session grid (centred + hint)Mission Control (GPU)When exactly one session is on the grid, its lone tile is centred in the window on its own width — a deliberately-placed little-window rather than a tile stranded in the top-left with an empty half beside it (the grid still floors a lone tile at the two-up 'little window' size so it never goes full-bleed, which would be indistinguishable from the maximized view — #143). A calm muted '⌘N to start another session' hint sits in the dead band below the centred tile, the single-session companion to the empty grid's 'right-click to begin · ⌘N' invitation, and — like it — lives in the passive underlay, breathing softly (static when animations are off) and passing every gesture straight through. Unlike the always-shown empty-grid invitation, the solo hint teaches once and then auto-dismisses after a short linger (session_presenter.solo_hint_linger_ms, default 5000 ms; 0 keeps it permanent), re-showing each time the grid returns to exactly one tile so it is not a fixture over the lone tile (#171). Both hints track the session count: zero → the invitation, one → this hint, two or more → nothing (a populated grid needs no prompt). The centring is Wingman's generic 'centre a solitary tile' grid option (layout.grid.solo_align), which kcode opts into; the hint content and the count-band policy are kcode's. #168. GPU build only.
Attention glow (grid tiles)Mission Control (GPU)In the grid, each session tile carries an ambient glow that reads its liveness at a glance, and the glow spills its colour past the tile edge into the surrounding gaps (a soft light-bleed). Routine activity — a session working, or armed to loop — glows the theme accent and breathes on a calm ~4s cycle; every active tile shares one clock, so they rise and fall together as a synced collective, keeping a busy grid calm. A tile that needs you breaks that calm: it switches hue and speeds up to an agitated ~1.6s breath — amber when it is waiting on your input (a permission ask or an agent question parked the turn on you — answering it, from any tile, returns the glow to working), red on an error — so the one tile that wants attention stands out from the synced field. Idle and stopped sessions are quiet: no glow, static and dark. With animations off, active tiles hold a steady lit glow instead of breathing. Rendered by Wingman; the liveness→colour mapping is kcode's.
Right-click (context menu)Mission Control (GPU)Summon a context-aware menu of actions at the cursor. Right-clicking a session tile lists that session's actions — Maximize, Compact, Stop, Detach; a tile in a group also gets the group actions — Focus group (maximizes just that cluster: its tiles fill the window side-by-side, every other tile hidden — Escape returns to the grid), Rename group (a text-editor overlay pre-filled with the current label; the new name persists and re-shows in the cluster's pill), and Ungroup (dissolves the whole cluster). Right-clicking empty grid lists the app actions — New session, Switch, Notifications, Stats, Commands (the palette), Settings, Theme, the layout modes (Grid / Tabbed / Sidebar), and the Animations toggle. It is a single vertical menu — every action in one column, scrolling when it outgrows the window (no ring, no overflow list). Navigate with ↑/↓ and Enter, or click a row directly; Esc dismisses. Every action maps to the same operation as its command-palette / shortcut equivalent. (Renamed from the cursor-radial 2026-08-30 — it has been a vertical menu, not a ring, since 2026-08-20.) GPU build only.
See #groups
Cmd-1 … Cmd-9 (jump to session)Mission Control (GPU)Open (maximize) the Nth session tile in Mission Control's grid order — Cmd-1 the first session, Cmd-2 the second, and so on through Cmd-9 — skipping any non-session tile. Works from the grid (blows that session up into the live window) or from another maximized session (jumps straight to the Nth), and is a no-op past the last session. A pure client-side view move — never a daemon round-trip, never persisted, exactly like a click-to-maximize. The shortcut lives in kcode — Wingman relays the unbound key. GPU build only.
Cmd-M / Cmd+PeriodMission Control (GPU)Cmd-M opens Mission Control — the grid overview — from a maximized session: the discoverable 'way home' chord (it replaced the old Cmd-1→grid now that Cmd-1…9 jump to sessions). Cmd+Period is the Wingman-bound synonym that also un-maximizes back to the grid, regardless of turn state (plain Esc stops a running turn, and itself backs out to the grid only when the session is idle — #168). (Cmd+Esc was tried first but macOS never delivers it to the app's key path.) 2026-08-08: a Cmd+. press also phantom-cancelled turns — root-caused via a symbolicated backtrace to AppKit's classic-Mac legacy-cancel synthesis (a Cmd+. press fabricates a bare-Escape key-equivalent NSEvent, which gpui replayed through its IME doCommandBySelector path as a real Escape action); fixed in the gpui fork (gpui_macos swallows a bare-escape key equivalent, which a physical Escape never is). The layout modes themselves — maximize a tile, tabbed — are switched from the on-screen mode buttons, the settings page, or the context menu, not number chords anymore. GPU build only.
Close a session tileMission Control (GPU)Detaches that session: the tile leaves the grid but its agent keeps running in the background, reattachable later. Closing never stops an agent — stopping is a separate, explicit action.
Drag a tile (reorder or group)Mission Control (GPU)Left-drag a session tile — no modifier needed. Pressing a tile lifts it immediately — the card brightens and gains a soft light halo (the Balatro-style pick-up) — so it reads as picked up before you move it, and the tile carried under the cursor is a compact lifted card bearing that tile's title. Drop it in a GAP to reorder the grid; drop it ONTO another tile to GROUP them into a free-form visual cluster (a soft themed wash + an editable label, see #groups). Drop onto any member of an existing group to add the tile to it; drag a grouped tile out into a gap to remove it. A group keeps its members contiguous through grid reflow. Manage a group (rename / ungroup) from the context menu on any of its tiles. The keyboard twins of reorder and the radial are Cmd-Ctrl-] / Cmd-Ctrl-[ and Cmd-Return on the Ctrl-Tab selection. GPU build only.
See #groups
Cmd-KMission Control (GPU)Open the recall switcher — a two-level, project-first overlay for bringing an off-grid session back onto the grid (only off-grid sessions appear; one already on the grid needs no recall). Level 1 lists the projects that have recall-able sessions — the detached and stopped ones, across all projects — ranked most-recent-first, each showing its off-grid session count; type to filter by project, Enter drills in. Level 2 lists that project's off-grid sessions under DETACHED / STOPPED headers (a liveness glyph, the session's manual title falling back to the project directory, and a muted recency for detached ones): Enter recalls the highlighted session — reattaching a detached one or resuming a stopped one — and ← backs out to the projects list (Esc dismisses the whole overlay). A drilled-in project shows its most-recent stopped sessions (session_presenter.switcher.recent_per_project, default 3) behind a 'show older (N)' toggle row; typing a search spans the full history past that cap. When exactly one project has recall-able sessions, level 1 is auto-skipped — ⌘K opens straight into that project's sessions. With nothing to recall the empty state reads 'No sessions to recall' (2026-07-27 — not 'No sessions', which read like data loss while a session was live). The switcher's shortcut lives in kcode — Wingman just relays the unbound key.
Cmd-PMission Control (GPU)Open the command palette: a flat, type-to-filter list of app / window / session actions, followed by your own skills and markdown commands (grouped under Skills / Commands — choosing one runs the same body as its / slash command, on the focused session) (#175). Every rebindable app shortcut has a row here (2026-07-26, the terminal design system's release checklist): besides New session / Switch session… / Notifications… / Providers… / Layout: Grid (⌘M) / Tabbed, the list carries Jump to latest message (⌘J), Next / Previous session (⌘⇧] / ⌘⇧[), Move tile later / earlier (⌘⌃] / ⌘⌃[) and Increase / Decrease / Reset text size (⌘+ / ⌘− / ⌘0) — eight actions that used to be reachable only from the keyboard. Each row shows its live chord, so a [keymap] rebind shows through and an unbound action reads . The two deliberate omissions are Tile actions (⌘↩ opens the right-click context menu — a menu, whose contents are already palette rows) and the ⌘1–9 jump family (Switch session… is the same operation by name). Like Cmd-K, the shortcut lives in kcode — Wingman just relays the unbound key.
Cmd-NMission Control (GPU)Start a new session — opens the in-app directory picker (2026-07-21; the same picker the context menu's 'New session' and the palette's 'New session' open): a themed two-pane browser modal in place of the old native macOS folder dialog. The breadcrumb up top jumps to any ancestor (the home prefix collapses to ~); the left sidebar lists Places (Home), your Favorites (star any directory with ⌘D or its row star — the list persists to session_presenter.favorite_dirs in config.toml, so favorites survive restarts and are hand-editable), and Recents (the projects kcode knows, most-recently-active first); the right pane lists the browsed directory's subdirectories. Typing filters the listing to matching subdirectories (2026-08-05 — a case-insensitive substring on the basename; the control rows hide so Enter always drills a match, ⌫ edits the query, the first esc clears it and only the second closes the picker). Keyboard: ↑/↓ move, ←/→ switch panes (← from the sidebar goes up a directory), Enter drills / jumps / opens, Backspace goes up (a .. row at the top of the listing does the same whenever the browsed directory has a parent — 2026-08-01), ⌘N creates a new folder inline (type the name, Enter — it's created inside the browsed directory and highlighted so a second Enter drills straight in), ⌘D stars the highlighted directory (on a sidebar Favorites row it un-favorites), ⌘⇧. toggles dot-directories (hidden by default, the Finder idiom). Mouse: the first click on a directory or place row selects it (the accent bar moves), a second click on the selected row drills / jumps — the file-browser idiom, so the folder you click is the folder ⌘D stars; click a row's ☆/★ to star/unstar it directly, 'Open this folder' picks the browsed directory, Esc or Cancel dismisses. Picking a folder spawns the session there and opens it maximized, ready to type; the picker feeds the same spawn path the native dialog did, so the project-trust gate applies identically. The native NSOpenPanel remains available — Settings (⌘,) › General › Directory picker cycles App ↔ macOS (session_presenter.dir_picker), live on the next ⌘N. Like Cmd-K / Cmd-P the shortcut lives in kcode — Wingman just relays the unbound key.
Cmd-Shift-NMission Control (GPU)Summon the notification center — an edge-anchored drawer sliding in from the right, listing the durable cross-project notification log newest-first (see the /notifications command). The same overlay the command palette's 'Notifications…' and the context menu's Notifications open. A glowing seam on the window's right edge, in the colour of the highest-severity unread notification, signals waiting notifications even while the drawer is closed; clicking the seam opens the center (it is a live affordance, not just an indicator), and visiting a session (focusing its tile) marks its notifications read and dims the seam. Clicking a notification row jumps to the session that raised it (reattaching or resuming it and maximizing its tile) and closes the drawer. Like Cmd-K / Cmd-P the shortcut lives in kcode — Wingman just relays the unbound key; distinct from the bare Cmd-N (new session). Dismiss with Escape or by clicking the dimmed backdrop. GPU build only.
Cmd-Shift-SMission Control (GPU)Summon the focused session's /stats dashboard (#70) — the overlay with that session's cost/token breakdown, per-turn token + context-occupancy sparklines, tool outcomes and top-tools chart, per-model cost, compaction/continuation/memory counters, and files touched. With no session focused (an empty grid or a non-session tile) it opens the /stats all cross-session roll-up instead — the only sensible stats view there. The same overlay the /stats command and the command palette's 'Stats' open. Like Cmd-K / Cmd-P the shortcut lives in kcode — Wingman just relays the unbound key; distinct from any bare Cmd-S. Dismiss with Escape. GPU build only.
See /stats
Cmd-TMission Control (GPU)Toggle the focused session's task/todo checklist overlay — one row per task with a bracketed status marker ([x] done, [•] in-progress, [ ] to-do) and its subject, the live view of the task/todo tool's list (see the /tasks command). The same overlay the /tasks command opens; pressing Cmd-T again (or Escape) dismisses it. With no session focused it is a no-op. Like Cmd-K / Cmd-P the shortcut lives in kcode — Wingman just relays the unbound key; distinct from any bare Cmd-T. GPU build only.
See /tasks task
Cmd-,Mission Control (GPU)Open the fullscreen settings page — the macOS Preferences chord, a synonym for /settings / /config and the context menu's Settings action. An app-level shortcut relayed by Wingman as an unbound key; Esc closes it back to the grid. GPU build only.
Ctrl-BMission Control (GPU)Open the keyboard-shortcuts cheatsheet — a static, read-only overlay listing kcode's app-level chords grouped into sections (Sessions, Mission Control, Panels, Text size, General). It is generated from the same keymap table that drives the real key dispatch, so it always matches the chords that actually work. ⌃B is a free chord (⌃C quits; every other Ctrl combo is unbound). The same overlay opens from the command palette's 'Keyboard shortcuts'. ↑/↓/PageUp/PageDown/Home/End scroll it when it outgrows the window at a large UI text scale. Dismiss with Escape. GPU build only.
See #shortcuts
Cmd-QMission Control (GPU)Quit the whole application (the standard macOS Quit chord) — a synonym for the command palette's Quit action. Unlike the other Mission Control chords, which Wingman relays as unbound keys, ⌘Q is wired through the macOS application menu's Quit item: macOS routes the reserved chord through the menu rather than the app's key path (the same reason Cmd+Esc was undeliverable), so the host registers a real app menu carrying Quit and binds ⌘Q to it. GPU build only.
Cmd-+ / Cmd-− / Cmd-0Mission Control (GPU)Zoom the UI text: Cmd-+ (or Cmd-=) grows the global text scale by 10%, Cmd-− shrinks it, Cmd-0 resets to 100% (50–300% range). Scales every font — chat, chrome, overlays — since they all read the theme's type scale; each change re-installs the active theme at the new scale and persists session_presenter.ui_scale. The same knob as the Settings page's General → Text size stepper. App-level shortcuts relayed by Wingman as unbound keys. GPU build only.
Mission Control (GPU, chat)
In-flight tool feedbackMission Control (GPU, chat)While a tool is running, its chat row reads as an action in progress rather than a finished one: a present-tense verb (Writing / Editing / Reading / Running / Searching / Fetching / Globbing …) led by a small animated braille spinner in the info tint — a live cue that the step is ongoing, not stalled, so a slow file write or command is never silent. The instant the tool settles the row reverts to its past-tense verb (Wrote / Edit / Read …) with a far-right ✓ (success) or ✗ (failure) outcome; a running bash or lsp row shows the spinner where that outcome glyph will land, then swaps it for the ✓/✗ on completion. With animations off the spinner holds its first frame (static) — the present-tense verb alone still distinguishes running from done. Gated by /show-tools like every tool row. #172. GPU build only. The pinned working strip above the composer names what the turn waits on (2026-08-06): a provider retry ('Provider not responding — retry 2/4 in 3s'), a park on you ('waiting on permission · bash (general) · 4m' or 'waiting on your answer') relayed by the daemon, live subagents ('2 agents working · web_fetch'), the agent's own set_activity line, or the generic Thinking…/Working… with a '· waiting Ns' stall clock — and Esc interrupts in every one of those states, including a permission park.
Chat text differentiation (by colour, not labels)Mission Control (GPU, chat)The four kinds of text in the transcript are told apart by colour, weight, and a band rather than author labels — the whole pane stays monospace (it is a terminal), so the distinction is tonal, not typographic. Your own message sits in a subtle accent-tinted band (a left accent border over a soft accent wash) with the message prose itself rendered in the accent colour — 'your words in your colour' — and no 'You' label. The model's reply is the default foreground with no band, reading as the clean answer. Reasoning (the ✦ Thought fold and its expanded text) is dim and italic — a visibly different register, an aside rather than the reply. Its expanded text is rendered Markdown, not flat text, then faded to the theme's emphasis.deemphasized level: code, lists and paths inside a reasoning trace keep their highlighting and structure and simply recede, rather than being flattened to one muted colour. The live trace and the settled fold draw through the same path, so reasoning never reformats itself when a turn settles. Tool rows are the code tone: a monospace header (verb + target) over their monospace output, led by a status-coloured glyph — the accent identity glyph when settled, the info-tinted spinner while running, a green ✓ or red ✗ on the outcome. Every tint resolves from the active theme's accent / foreground / muted roles, so a /theme switch re-colours the whole scheme automatically. Vertical space follows the same logic: a row takes air only where the pair of rows calls for it, so prose turns and open tool output stay airy while runs of one-liners — collapsed folds, finished tool rows, control-plane lines — group tightly into a block instead of each floating on the same fixed gap. #174. GPU build only. The transcript also nests by indent (2026-08-06): your message at level 0 with its ▾ turn-control gutter, the assistant's reply one level in (its text lining up under yours), and the reasoning fold a second level in behind a muted left rule — a turn reads as prompt → nested response, not a flush-left wall.
Welcome card (first run)Mission Control (GPU, chat)A fresh session's transcript opens on a centred welcome card naming the app-level chords (resolved against your live [keymap], so rebinds re-print correctly) — it yields to the composer on your first submit, the caret smear snapping from the card down to the bottom chrome. When the presenter reports that no provider is keyed (2026-08-01), the card carries a first-run setup guide instead: one muted explainer line plus an accent Set up a provider call-to-action that deep-links straight into Settings (⌘,) › Providers, so a brand-new install is one click from connecting a model.
Mission Control (GPU, maximized chat)
EscMission Control (GPU, maximized chat)Stop the focused session's in-flight turn (Esc=stop-turn, mirroring the terminal UI and opencode/claude-code) — but only as the base layer: an open slash menu or overlay (switcher, palette, picker, /control) is dismissed first, one layer per press, and the turn is interrupted only once nothing else is on top. When the session is idle (no turn running), Esc instead backs out to Mission Control — the grid overview (#168 — the keyboard twin of ⌘M; an idle stop-turn was a no-op, so nothing is lost). The panel captures Esc in the capture phase so the focused input never swallows it; the presenter then arbitrates (overlay dismiss → turn interrupt → back to grid). Cmd+Period and Cmd-M remain the dedicated 'way home' chords that un-maximize regardless of turn state. A turn parked on a permission or ask-user prompt is interruptible too (2026-08-06): Esc denies the open prompt when one is rendered in this pane, and otherwise sends the interrupt — the daemon denies the parked asks and cancels every subagent the turn spawned, so a parked turn can no longer outlive Esc.
Cmd-J (jump to your last message)Mission Control (GPU, maximized chat)Scroll the transcript straight to your most recent message — the last instruction — and select it with the same accent highlight a click gives, so you can snap back to what you asked while the agent's reply streams below. A no-op when the conversation has no message from you yet. Not Cmd-↑: the chat input binds Cmd-↑ to 'move cursor to input start', so the jump uses Cmd-J ('jump'). The same jump is also offered as a contextual '↑ your last message' pill that appears at the top of the transcript whenever that message has scrolled above the viewport, and hides again as you scroll it back into view; clicking the pill does the same jump. The direct-chord twin of the palette's 'Jump to message…' picker, targeting your last message with no overlay. A pure client-side view move, never a daemon round-trip; the chord lives in kcode (Wingman relays the unbound key) and is listed in the Ctrl-B keyboard cheatsheet under General. GPU build only.
/ then typeMission Control (GPU, maximized chat)Typing / at the start of a maximized session's chat input opens kcode's slash-command menu, listing the built-in commands filtered by what you type. The menu opens upward, above the input — kcode's own surface, because gpui-component's completion popover only anchors below the cursor and a bottom-pinned input would clip it. ↑ / ↓ move the highlight (wrapping, via a capture-phase action that preempts the input's own cursor move); Tab completes the highlighted command into the input without running it; Enter runs the highlighted command — only the arg-taking few (/goal, /loop, /rename, /worktree, /add-dir) complete into the input instead, ready for their argument (2026-08-06: no more mandatory second Enter). Esc closes the menu. The command is sent to the session as free text. The menu lists the documented built-ins first (aliases folded), then your own skills and markdown commands (from skill_dirs / command_dirs) — each tagged skill or cmd so it reads as distinct — so they're discoverable rather than typed blind (#175); a user entry whose name shadows a built-in or an earlier skill is dropped (dispatch precedence: built-in → skill → command). Daemon-authoritative + plugin commands remain a follow-on.
~/ then typeMission Control (GPU, maximized chat)Typing a token that starts with ~/ in a maximized session's chat input opens a path-completion dropdown above the input (2026-08-12), listing the matching files and directories under your home — the shell's ~/… completion, in the composer. The current ~ token (the one under the caret) completes in place; text before and after it is untouched, so a path mid-sentence completes too. ↑ / ↓ move the highlight; Tab or Enter accepts the highlighted path (2026-09-04 — Enter used to submit the half-typed token as a message); Esc closes the dropdown. A directory completes with a trailing / and the dropdown stays open so you can drill into it; a file completes and closes. Prefix match on the in-progress segment, case-insensitive, directories listed before files. Dotfiles are hidden unless you type the dot. The menu re-reads the directory on every keystroke, so a path that stops existing mid-type just closes the menu (never an error). GPU build only.
Drag to select textMission Control (GPU, maximized chat)Click-drag across a message's text to select it character-by-character; the dragged characters highlight (theme accent wash, painted under the glyphs). The drag can span messages and tool blocks: a selection crossing block boundaries highlights the anchor's tail, every whole block in between, and the head block's head — one continuous transcript-wide selection. Tool blocks are selectable too — a diff's code (the line-number gutter and the +/- markers stay chrome, excluded), bash output / command / intent lines, and syntax-highlighted file content — each rendered line a unit, keeping its colours. Inline math contributes its LaTeX source ($…$) and images their alt text, so a span across them copies sensibly. Releasing does NOT auto-copy: press ⌘C to copy (native macOS — the highlight is the feedback, the clipboard only changes on ⌘C). Click elsewhere to deselect. A single click (no drag) selects the whole message instead.
Cmd-CMission Control (GPU, maximized chat)Copy to the clipboard, silently (no flash): the active character selection if there is one, otherwise the whole click-selected message's body. A selection within a single block copies the bare slice; a selection spanning blocks copies each block's slice under a ### {header} markdown header — ### user / ### assistant (lowercase role) for a message, ### tool for a tool block — the same convention the session .md export writes, in transcript order, blank line between blocks. With neither selected, ⌘C falls through to the chat input's own copy. Precedence: character selection → selected message → input. Mirrors the terminal UI's copy, native-macOS style.
Mission Control (GPU, maximized)
Cmd-Shift-] / Cmd-Shift-[Mission Control (GPU, maximized)Move the maximized tile to the next (Cmd-Shift-]) or previous (Cmd-Shift-[) session in the grid order, wrapping around the ends — so you move session-to-session without dropping back to the grid. Focus-mode only (in the grid there's no maximized tile to move); a pure client-side focus change that never touches the agents. Like Cmd-K / Cmd-P the shortcut lives in kcode — Wingman just relays the unbound key. Plain Cmd-[ / Cmd-] without Shift stay with the chat input.
Mission Control (GPU, grid)
Ctrl-Tab / Ctrl-Shift-TabMission Control (GPU, grid)Move the grid's keyboard selection to the next (Ctrl-Tab) or previous (Ctrl-Shift-Tab) tile — the selected tile is ringed with a crisp accent outline so you can see, without the mouse, which tile the keyboard will act on. The selection is the anchor for the keyboard tile gestures: Cmd-Ctrl-] / Cmd-Ctrl-[ reorder the selected tile and Cmd-Return opens its context menu. A pure client-side view move, never a daemon round-trip. Wingman owns the Ctrl-Tab → move-selection binding; kcode draws the ring. GPU build only.
Cmd-Ctrl-] / Cmd-Ctrl-[Mission Control (GPU, grid)Reorder the selected grid tile (see Ctrl-Tab) one place later (Cmd-Ctrl-]) or earlier (Cmd-Ctrl-[) in the grid order — the keyboard twin of dragging a tile into a gap. A no-op at the far end (it does not wrap) and outside the grid (there is no grid selection to move in a maximized / tabbed view). Distinct from Cmd-Shift-] / Cmd-Shift-[, which jump the maximized tile session-to-session in Focus mode. A pure client-side reorder that never touches the agents; the shortcut lives in kcode — Wingman just relays the unbound key. GPU build only.
Cmd-ReturnMission Control (GPU, grid)Open the context menu (the tile action menu) on the selected grid tile (see Ctrl-Tab) — the keyboard twin of right-clicking a tile. With no tile selected it opens the app-context radial, as a right-click on empty grid does. A keyboard summon carries no cursor point, so the ring centres in the window; navigate it with the arrow keys + Enter and Esc to dismiss, exactly like the right-click radial (see 'Right-click (context menu)'). The shortcut lives in kcode — Wingman just relays the unbound key. GPU build only.
See #groups
Tile identity (window-edge HUD + footer title)Mission Control (GPU, grid)Grid cards carry no title chrome. A tile's identity surfaces two ways instead: (1) the window-edge HUD — in the grid, the name of the tile under the pointer (or, with the pointer at rest, the ctrl-tab selection) appears as one muted line at the window's bottom edge, in the same typographic voice as the chat status bar; at most one label is ever shown and the grid is label-free at rest. (2) A session you /rename leads its own chat status bar with the title (title · dir:branch · model · state), and because every grid miniature renders that same footer, a renamed tile is legible in its own card. A session's title is its project directory unless explicitly set with /rename — there is no auto-generated title. Rename via /rename or the switcher's Ctrl+R. GPU build only.
Mission Control (GPU, switcher)
Type, ↑ / ↓ then EnterMission Control (GPU, switcher)Type to fuzzy-filter the current level (case-insensitive subsequence match — project names at level 1, session titles at a drilled-in project), ↑ / ↓ to move the highlight through the filtered rows (wrapping), Backspace to edit the query. At the projects level Enter drills into the highlighted project; at a drilled-in project Enter recalls the highlighted session (reattach a detached one, resume a stopped one) and ← backs out to the projects list. Esc closes the switcher without recalling. At a drilled-in project, typing also widens the list to the full stopped history past the recent cap.
Ctrl+DMission Control (GPU, switcher)At a drilled-in project, permanently delete the highlighted session — its durable row (with the /model, /effort, … overrides), event log, and message buffer. Raises a confirmation modal first (Cancel highlighted by default); accepting stops the agent if it is live, then erases the session everywhere and removes its tile. Irreversible and NOT resumable — contrast Enter (reattach/resume) and /stop (resumable with --resume). Mirrors opencode's ctrl+d delete-session.
Ctrl+RMission Control (GPU, switcher)At a drilled-in project, rename the highlighted session in place: opens an edit prompt seeded with its current title (or the project-name fallback), typing edits it, Enter commits the new title (shown in the switcher and the tile chrome), and an empty buffer clears it back to the project name. Esc — which dismisses the whole switcher — backs out without committing; only Enter sends a rename. The same daemon-side rename as the /rename command.
Ctrl+Shift+DMission Control (GPU, switcher)At a drilled-in project, clear that project's stopped sessions at once — its accumulated pile of finished sessions. Raises a confirmation modal naming the count (e.g. 'Clear 5 stopped'); accepting permanently deletes each stopped session (row + events + buffer). A no-op when the project has nothing stopped. The switcher footer shows the live stopped count next to this key.
Mission Control (GPU, palette)
Type, ↑ / ↓ then EnterMission Control (GPU, palette)Type to filter the palette (case-insensitive substring match on the label), ↑ / ↓ to move the highlight (wrapping), Enter to run the highlighted action, Backspace to edit the query. Esc closes the palette without running anything. Commands include New session, Switch session, Notifications, Providers… (provider status, API keys and models — app-level, formerly the /providers slash command; with a session focused its model-picker tail targets that session, otherwise it opens Settings › Providers), Layout: Grid / Tabbed, Toggle thinking / tool calls / timestamps / animations, Switch theme / Switch to light mode / Switch to dark mode / Switch to high contrast, Stop / Close / Compact session, Help, Quit.
Mission Control (GPU, settings)
Settings page (sidebar + panes)Mission Control (GPU, settings)The fullscreen settings page raised over the grid: a left sidebar of sections and a content pane of live controls for the highlighted section. Click a sidebar row to switch sections, or drive it from the keyboard sidebar-primary: ↑/↓ move the sidebar section, → or Enter dives into its rows, ↑/↓ then move the row and ← steps back out (or nudges a numeric stepper down). Tab is not a section switch here — the GPU window's framework binds it to focus-cycling. General: Default model (click to open a searchable picker of every keyed provider's models plus a “Provider default” option — the pick persists default_model and relays a live SetDefaultModel to the daemon so new sessions use it without a restart), Layout (click the value to cycle Grid→Tabbed→Sidebar — Sidebar is the macOS split view: a left rail lists every session and the focused session fills the rest), Directory picker (click to cycle App ↔ macOS — the in-app browser modal vs the native folder dialog for ⌘N, persisted to session_presenter.dir_picker and live on the next open), Settings navigation (click to cycle Sidebar ↔ Tabs — the page's section nav as the macOS-System-Settings left rail or a horizontal tab bar under the title, persisted to session_presenter.settings_nav and re-laid out in place on the open page), Max grid (− / + steppers, 1–8), Text size (− / + steppers, the 50–300% UI text scale — same knob as Cmd-+/Cmd-−), UI font and Mono font (each click opens a searchable picker of every font installed on your machine plus a “Theme default” option — the pick persists to session_presenter.ui_font_family / session_presenter.mono_font_family and re-installs the active theme so the new font applies live across chrome and content), and the ambient-backdrop knobs (Ambient backdrop on/off, Backdrop style cycle, Backdrop reacts to activity, Per-tile activity light). Advanced: Command dirs and Skill dirs — click a value to open the text-editor overlay (type a comma-separated path list, Enter commits and an empty buffer reverts to the default; either way — commit or Esc — the editor peels off and the page resumes at Advanced (the overlay stack keeps it mounted underneath)) — plus Memory auto-recall, a toggle for the per-turn memory.auto_recall gate (off by default): the flip persists to config.toml and relays live through the daemon's generic ApplySettings relay, so running sessions honour it at their next turn (2026-08-13). Providers: a searchable list of every keyable LLM provider (the six native vendors first, then any OpenAI-compatible vendor), each with a Connected / Add-key badge — type to filter, ↑/↓ to move the highlight, click or Enter a row to open a masked key prompt (⌘V to paste; Enter saves to ~/.kcode/auth.toml mode 0600 and marks it Connected at once, Esc cancels; either way the prompt peels off and the page resumes at Providers). Plugins: the installed-plugin inventory ending in "+ Add plugin from folder…" — Enter a plugin for its detail (Enabled toggle, the schema-generated settings form, source + permissions, the ~1s activity tail, Remove with a confirm for managed folders); Esc or ← leaves the detail back to the list before a second Esc closes the page. Provider keys live here rather than on a session command because one key serves every session. Layout + grid persist to the daemon workspace; text size + dirs persist to config.toml and keys to auth.toml, all off the render frame. Esc from the page closes it back to the grid. The persistent startup default model lives here (all-session — what every new session boots on); the per-session model override and the other session-varying knobs are session slash commands (/theme, /model, /effort, /cot, …), not here. The title bar's search box filters rows, not just sections (2026-08-30, the macOS System Settings idiom): typing narrows the sidebar to the matching settings grouped under their section headers — matching on the row's label, its config key, and authored synonyms — and the content pane previews the highlighted hit's section. ↑/↓ walk the hits; Enter (or a click) jumps straight to that row — the query clears, the section is selected, and the row is highlighted with the keyboard in the content pane. Esc clears the search (then closes). The index is rebuilt from the panels' live rows on each keystroke, so async-filled sections (Plugins, Providers) are findable the moment their rows land. GPU build only.
Trust dialog
← / → / ↑ / ↓ / TabTrust dialogMove the highlighted choice on the project-trust dialog (wrapping; Tab steps forward). Opens highlighting This session only — the safe default. See the Permission Modes section's Project trust entry for what the dialog gates.
EnterTrust dialogChoose the highlighted button (Trust & remember / This session only / Open untrusted).
EscTrust dialogOn a spawn/resume gate: cancel the action outright (invoking it again re-asks). On the boot prompt: open this window untrusted — the project's .kcode content stays ignored.