v0.2.99 4d92b3d

models.go RawHistoryFolder

1package provider
2
3import (
4	"fmt"
5	"sync"
6)
7
8// Model describes a single LLM we know about.
9type Model struct {
10	Provider      string // "anthropic" | "openai"
11	ID            string // API id
12	DisplayName   string
13	API           string // wire API override for providers that support multiple protocols
14	ContextWindow int
15	MaxOutput     int
16	Reasoning     bool // supports reasoning/thinking
17
18	// AdaptiveThinking marks Anthropic models that only support the
19	// adaptive thinking mode (Opus 4.7+). These reject explicit
20	// thinking budgets (thinking:{type:"enabled",budget_tokens:N} -> 400)
21	// and also reject non-default sampling params (temperature/top_p/
22	// top_k). The Anthropic client sends thinking:{type:"adaptive"} plus
23	// output_config.effort and omits temperature for these models.
24	AdaptiveThinking bool
25
26	// Prices are USD per 1M tokens.
27	PriceInput      float64
28	PriceOutput     float64
29	PriceCacheRead  float64
30	PriceCacheWrite float64
31
32	// Speculative marks models whose ids are known from the upstream
33	// vendor's CLI but not yet live on their public API. They'll 404
34	// today but start working the moment the provider flips the switch.
35	Speculative bool
36
37	// BaseURL overrides the provider's default API endpoint for this
38	// model. Optional; when empty the provider's default (or the
39	// --base-url flag) is used. Useful for local models served by
40	// ollama, vLLM, LM Studio, etc.
41	BaseURL string
42
43	// Source is where this model entry came from: "catalog" (baked in),
44	// "live" (discovered via /v1/models), or "cache" (loaded from the
45	// on-disk cache). Informational.
46	Source string
47}
48
49// Catalog is the hardcoded, read-only list of supported models.
50// Prices are USD per 1M tokens. The list is curated to what zot's
51// clients (Anthropic Messages + OpenAI Chat Completions) can actually
52// talk to; models that are only reachable through the OpenAI Responses
53// API (o1-pro, o3-pro, gpt-5-pro) are omitted.
54var Catalog = []Model{
55	// ---- Anthropic / Claude 4.x ----
56	{
57		Provider: "anthropic", ID: "claude-sonnet-4-5", DisplayName: "Claude Sonnet 4.5 (latest)",
58		ContextWindow: 200000, MaxOutput: 64000, Reasoning: true,
59		PriceInput: 3, PriceOutput: 15, PriceCacheRead: 0.3, PriceCacheWrite: 3.75,
60	},
61	{
62		Provider: "anthropic", ID: "claude-opus-4-1", DisplayName: "Claude Opus 4.1 (latest)",
63		ContextWindow: 200000, MaxOutput: 32000, Reasoning: true,
64		PriceInput: 15, PriceOutput: 75, PriceCacheRead: 1.5, PriceCacheWrite: 18.75,
65	},
66	{
67		Provider: "anthropic", ID: "claude-opus-4-0", DisplayName: "Claude Opus 4 (latest)",
68		ContextWindow: 200000, MaxOutput: 32000, Reasoning: true,
69		PriceInput: 15, PriceOutput: 75, PriceCacheRead: 1.5, PriceCacheWrite: 18.75,
70	},
71	{
72		Provider: "anthropic", ID: "claude-sonnet-4-0", DisplayName: "Claude Sonnet 4 (latest)",
73		ContextWindow: 200000, MaxOutput: 64000, Reasoning: true,
74		PriceInput: 3, PriceOutput: 15, PriceCacheRead: 0.3, PriceCacheWrite: 3.75,
75	},
76	{
77		Provider: "anthropic", ID: "claude-haiku-4-5", DisplayName: "Claude Haiku 4.5 (latest)",
78		ContextWindow: 200000, MaxOutput: 64000, Reasoning: true,
79		PriceInput: 1, PriceOutput: 5, PriceCacheRead: 0.1, PriceCacheWrite: 1.25,
80	},
81
82	// ---- Anthropic / Claude 3.x (legacy) ----
83	{
84		Provider: "anthropic", ID: "claude-3-7-sonnet-20250219", DisplayName: "Claude Sonnet 3.7",
85		ContextWindow: 200000, MaxOutput: 64000, Reasoning: true,
86		PriceInput: 3, PriceOutput: 15, PriceCacheRead: 0.3, PriceCacheWrite: 3.75,
87	},
88	{
89		Provider: "anthropic", ID: "claude-3-5-sonnet-20241022", DisplayName: "Claude Sonnet 3.5 v2",
90		ContextWindow: 200000, MaxOutput: 8192, Reasoning: false,
91		PriceInput: 3, PriceOutput: 15, PriceCacheRead: 0.3, PriceCacheWrite: 3.75,
92	},
93	{
94		Provider: "anthropic", ID: "claude-3-5-haiku-latest", DisplayName: "Claude Haiku 3.5 (latest)",
95		ContextWindow: 200000, MaxOutput: 8192, Reasoning: false,
96		PriceInput: 0.8, PriceOutput: 4, PriceCacheRead: 0.08, PriceCacheWrite: 1,
97	},
98	{
99		Provider: "anthropic", ID: "claude-3-opus-20240229", DisplayName: "Claude Opus 3",
100		ContextWindow: 200000, MaxOutput: 4096, Reasoning: false,
101		PriceInput: 15, PriceOutput: 75, PriceCacheRead: 1.5, PriceCacheWrite: 18.75,
102	},
103
104	// ---- DeepSeek ----
105	// The current public DeepSeek API exposes the V4 family on
106	// api.deepseek.com/v1. Pro is the flagship reasoning model;
107	// Flash is the cheaper/faster sibling. Both accept image inputs
108	// (multimodal parts: image_url) in addition to text.
109	{
110		Provider: "deepseek", ID: "deepseek-v4-pro", DisplayName: "DeepSeek V4 Pro",
111		ContextWindow: 1000000, MaxOutput: 384000, Reasoning: true,
112		PriceInput: 0.435, PriceOutput: 0.87, PriceCacheRead: 0.003625,
113		BaseURL: "https://api.deepseek.com",
114	},
115	{
116		Provider: "deepseek", ID: "deepseek-v4-flash", DisplayName: "DeepSeek V4 Flash",
117		ContextWindow: 1000000, MaxOutput: 384000, Reasoning: true,
118		PriceInput: 0.14, PriceOutput: 0.28, PriceCacheRead: 0.0028,
119		BaseURL: "https://api.deepseek.com",
120	},
121
122	// ---- Kimi / Kimi Code ----
123	// Anthropic-messages on https://api.kimi.com/coding (no /v1 suffix;
124	// the Anthropic client appends /v1/messages itself).
125	{
126		Provider: "kimi", ID: "kimi-for-coding", DisplayName: "Kimi For Coding",
127		ContextWindow: 262144, MaxOutput: 32768, Reasoning: true,
128		PriceInput: 0, PriceOutput: 0, PriceCacheRead: 0,
129		BaseURL: "https://api.kimi.com/coding",
130	},
131
132	// ---- OpenAI / GPT-5 family ----
133	{
134		Provider: "openai", ID: "gpt-5", DisplayName: "GPT-5",
135		ContextWindow: 400000, MaxOutput: 128000, Reasoning: true,
136		PriceInput: 1.25, PriceOutput: 10, PriceCacheRead: 0.125,
137	},
138	{
139		Provider: "openai", ID: "gpt-5-mini", DisplayName: "GPT-5 Mini",
140		ContextWindow: 400000, MaxOutput: 128000, Reasoning: true,
141		PriceInput: 0.25, PriceOutput: 2, PriceCacheRead: 0.025,
142	},
143	{
144		Provider: "openai", ID: "gpt-5-nano", DisplayName: "GPT-5 Nano",
145		ContextWindow: 400000, MaxOutput: 128000, Reasoning: true,
146		PriceInput: 0.05, PriceOutput: 0.4, PriceCacheRead: 0.005,
147	},
148
149	// ---- OpenAI / GPT-4.1 family ----
150	{
151		Provider: "openai", ID: "gpt-4.1", DisplayName: "GPT-4.1",
152		ContextWindow: 1047576, MaxOutput: 32768, Reasoning: false,
153		PriceInput: 2, PriceOutput: 8, PriceCacheRead: 0.5,
154	},
155	{
156		Provider: "openai", ID: "gpt-4.1-mini", DisplayName: "GPT-4.1 mini",
157		ContextWindow: 1047576, MaxOutput: 32768, Reasoning: false,
158		PriceInput: 0.4, PriceOutput: 1.6, PriceCacheRead: 0.1,
159	},
160	{
161		Provider: "openai", ID: "gpt-4.1-nano", DisplayName: "GPT-4.1 nano",
162		ContextWindow: 1047576, MaxOutput: 32768, Reasoning: false,
163		PriceInput: 0.1, PriceOutput: 0.4, PriceCacheRead: 0.03,
164	},
165
166	// ---- OpenAI / GPT-4o family ----
167	{
168		Provider: "openai", ID: "gpt-4o", DisplayName: "GPT-4o",
169		ContextWindow: 128000, MaxOutput: 16384, Reasoning: false,
170		PriceInput: 2.5, PriceOutput: 10, PriceCacheRead: 1.25,
171	},
172	{
173		Provider: "openai", ID: "gpt-4o-mini", DisplayName: "GPT-4o mini",
174		ContextWindow: 128000, MaxOutput: 16384, Reasoning: false,
175		PriceInput: 0.15, PriceOutput: 0.6, PriceCacheRead: 0.08,
176	},
177
178	// ---- OpenAI / reasoning models ----
179	{
180		Provider: "openai", ID: "o4-mini", DisplayName: "o4-mini",
181		ContextWindow: 200000, MaxOutput: 100000, Reasoning: true,
182		PriceInput: 1.1, PriceOutput: 4.4, PriceCacheRead: 0.28,
183	},
184	{
185		Provider: "openai", ID: "o3", DisplayName: "o3",
186		ContextWindow: 200000, MaxOutput: 100000, Reasoning: true,
187		PriceInput: 2, PriceOutput: 8, PriceCacheRead: 0.5,
188	},
189	{
190		Provider: "openai", ID: "o3-mini", DisplayName: "o3-mini",
191		ContextWindow: 200000, MaxOutput: 100000, Reasoning: true,
192		PriceInput: 1.1, PriceOutput: 4.4, PriceCacheRead: 0.55,
193	},
194	{
195		Provider: "openai", ID: "o1", DisplayName: "o1",
196		ContextWindow: 200000, MaxOutput: 100000, Reasoning: true,
197		PriceInput: 15, PriceOutput: 60, PriceCacheRead: 7.5,
198	},
199
200	// ---- Google / Gemini ----
201	{
202		Provider: "google", ID: "gemini-2.5-pro", DisplayName: "Gemini 2.5 Pro",
203		ContextWindow: 1048576, MaxOutput: 65536, Reasoning: true,
204		PriceInput: 1.25, PriceOutput: 10, PriceCacheRead: 0.125,
205	},
206	{
207		Provider: "google", ID: "gemini-2.5-flash", DisplayName: "Gemini 2.5 Flash",
208		ContextWindow: 1048576, MaxOutput: 65536, Reasoning: true,
209		PriceInput: 0.3, PriceOutput: 2.5, PriceCacheRead: 0.03,
210	},
211	{
212		Provider: "google", ID: "gemini-2.5-flash-lite", DisplayName: "Gemini 2.5 Flash-Lite",
213		ContextWindow: 1048576, MaxOutput: 65536, Reasoning: true,
214		PriceInput: 0.1, PriceOutput: 0.4, PriceCacheRead: 0.01,
215	},
216	{
217		Provider: "google", ID: "gemini-2.0-flash", DisplayName: "Gemini 2.0 Flash",
218		ContextWindow: 1048576, MaxOutput: 8192, Reasoning: false,
219		PriceInput: 0.1, PriceOutput: 0.4, PriceCacheRead: 0.025,
220	},
221	{
222		Provider: "google", ID: "gemini-2.0-flash-lite", DisplayName: "Gemini 2.0 Flash-Lite",
223		ContextWindow: 1048576, MaxOutput: 8192, Reasoning: false,
224		PriceInput: 0.075, PriceOutput: 0.3, PriceCacheRead: 0,
225	},
226
227	// ---- OpenRouter ----
228	// Seed entry only: the default model returned by defaultModelForProvider
229	// so it resolves offline. The full OpenRouter catalog is discovered live
230	// (DiscoverOpenRouter) and overlays this on refresh.
231	{
232		Provider: "openrouter", ID: "anthropic/claude-sonnet-4.5", DisplayName: "Claude Sonnet 4.5 (OpenRouter)",
233		ContextWindow: 1000000, MaxOutput: 64000, Reasoning: true,
234		PriceInput: 3, PriceOutput: 15, PriceCacheRead: 0.3, PriceCacheWrite: 3.75,
235		BaseURL: openrouterDefaultBaseURL,
236	},
237	{
238		Provider: "openrouter", ID: "anthropic/claude-sonnet-5", DisplayName: "Claude Sonnet 5 (OpenRouter)",
239		ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, AdaptiveThinking: true,
240		PriceInput: 2, PriceOutput: 10, PriceCacheRead: 0.2, PriceCacheWrite: 2.5,
241		BaseURL: openrouterDefaultBaseURL,
242	},
243
244	// ---- Speculative: Anthropic ----
245	{
246		Provider: "anthropic", ID: "claude-opus-4-5", DisplayName: "Claude Opus 4.5 (latest)",
247		ContextWindow: 200000, MaxOutput: 64000, Reasoning: true,
248		PriceInput: 5, PriceOutput: 25, PriceCacheRead: 0.5, PriceCacheWrite: 6.25,
249		Speculative: true,
250	},
251	{
252		Provider: "anthropic", ID: "claude-opus-4-6", DisplayName: "Claude Opus 4.6",
253		ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true,
254		PriceInput: 5, PriceOutput: 25, PriceCacheRead: 0.5, PriceCacheWrite: 6.25,
255		Speculative: true,
256	},
257	{
258		Provider: "anthropic", ID: "claude-opus-4-7", DisplayName: "Claude Opus 4.7",
259		ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, AdaptiveThinking: true,
260		PriceInput: 5, PriceOutput: 25, PriceCacheRead: 0.5, PriceCacheWrite: 6.25,
261		Speculative: true,
262	},
263	{
264		Provider: "anthropic", ID: "claude-opus-4-8", DisplayName: "Claude Opus 4.8",
265		ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, AdaptiveThinking: true,
266		PriceInput: 5, PriceOutput: 25, PriceCacheRead: 0.5, PriceCacheWrite: 6.25,
267		Speculative: true,
268	},
269	{
270		Provider: "anthropic", ID: "claude-sonnet-4-6", DisplayName: "Claude Sonnet 4.6",
271		ContextWindow: 1000000, MaxOutput: 64000, Reasoning: true,
272		PriceInput: 3, PriceOutput: 15, PriceCacheRead: 0.3, PriceCacheWrite: 3.75,
273		Speculative: true,
274	},
275	{
276		Provider: "anthropic", ID: "claude-sonnet-5", DisplayName: "Claude Sonnet 5",
277		ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, AdaptiveThinking: true,
278		PriceInput: 2, PriceOutput: 10, PriceCacheRead: 0.2, PriceCacheWrite: 2.5,
279	},
280	{
281		Provider: "anthropic", ID: "claude-fable-5", DisplayName: "Claude Fable 5",
282		ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, AdaptiveThinking: true,
283		PriceInput: 10, PriceOutput: 50, PriceCacheRead: 0.5, PriceCacheWrite: 6.25,
284		Speculative: true,
285	},
286
287	// ---- Speculative: OpenAI ----
288	// Public OpenAI API route. The ChatGPT/Codex subscription route is
289	// represented separately below as provider "openai-codex".
290	{
291		Provider: "openai", ID: "gpt-5.1", DisplayName: "GPT-5.1",
292		ContextWindow: 400000, MaxOutput: 128000, Reasoning: true,
293		PriceInput: 1.25, PriceOutput: 10, PriceCacheRead: 0.13,
294		Speculative: true,
295	},
296	{
297		Provider: "openai", ID: "gpt-5.2", DisplayName: "GPT-5.2",
298		ContextWindow: 400000, MaxOutput: 128000, Reasoning: true,
299		PriceInput: 1.75, PriceOutput: 14, PriceCacheRead: 0.175,
300		Speculative: true,
301	},
302	{
303		Provider: "openai", ID: "gpt-5.4", DisplayName: "GPT-5.4",
304		ContextWindow: 272000, MaxOutput: 128000, Reasoning: true,
305		PriceInput: 2.5, PriceOutput: 15, PriceCacheRead: 0.25,
306		Speculative: true,
307	},
308	{
309		Provider: "openai", ID: "gpt-5.4-mini", DisplayName: "GPT-5.4 mini",
310		ContextWindow: 400000, MaxOutput: 128000, Reasoning: true,
311		PriceInput: 0.75, PriceOutput: 4.5, PriceCacheRead: 0.075,
312		Speculative: true,
313	},
314	{
315		Provider: "openai", ID: "gpt-5.5", DisplayName: "GPT-5.5",
316		ContextWindow: 272000, MaxOutput: 128000, Reasoning: true,
317		PriceInput: 5, PriceOutput: 30, PriceCacheRead: 0.5,
318		Speculative: true,
319	},
320	{
321		Provider: "openai", ID: "gpt-5.6-luna", DisplayName: "GPT-5.6 Luna", API: APIResponses,
322		ContextWindow: 272000, MaxOutput: 128000, Reasoning: true,
323		PriceInput: 1, PriceOutput: 6, PriceCacheRead: 0.1, PriceCacheWrite: 1.25,
324		Speculative: true,
325	},
326	{
327		Provider: "openai", ID: "gpt-5.6-sol", DisplayName: "GPT-5.6 Sol", API: APIResponses,
328		ContextWindow: 272000, MaxOutput: 128000, Reasoning: true,
329		PriceInput: 5, PriceOutput: 30, PriceCacheRead: 0.5, PriceCacheWrite: 6.25,
330		Speculative: true,
331	},
332	{
333		Provider: "openai", ID: "gpt-5.6-terra", DisplayName: "GPT-5.6 Terra", API: APIResponses,
334		ContextWindow: 272000, MaxOutput: 128000, Reasoning: true,
335		PriceInput: 2.5, PriceOutput: 15, PriceCacheRead: 0.25, PriceCacheWrite: 3.125,
336		Speculative: true,
337	},
338
339	// ---- OpenAI Codex / ChatGPT subscription backend ----
340	// Same model ids as the OpenAI family, but routed through the
341	// ChatGPT Codex OAuth backend rather than api.openai.com.
342	{
343		Provider: "openai-codex", ID: "gpt-5.3-codex-spark", DisplayName: "GPT-5.3 Codex Spark",
344		ContextWindow: 272000, MaxOutput: 128000, Reasoning: true,
345		PriceInput: 1.75, PriceOutput: 14, PriceCacheRead: 0.175,
346		Speculative: true,
347	},
348	{
349		Provider: "openai-codex", ID: "gpt-5.4", DisplayName: "GPT-5.4",
350		ContextWindow: 272000, MaxOutput: 128000, Reasoning: true,
351		PriceInput: 2.5, PriceOutput: 15, PriceCacheRead: 0.25,
352		Speculative: true,
353	},
354	{
355		Provider: "openai-codex", ID: "gpt-5.4-mini", DisplayName: "GPT-5.4 mini",
356		ContextWindow: 272000, MaxOutput: 128000, Reasoning: true,
357		PriceInput: 0.75, PriceOutput: 4.5, PriceCacheRead: 0.075,
358		Speculative: true,
359	},
360	{
361		Provider: "openai-codex", ID: "gpt-5.5", DisplayName: "GPT-5.5",
362		ContextWindow: 272000, MaxOutput: 128000, Reasoning: true,
363		PriceInput: 5, PriceOutput: 30, PriceCacheRead: 0.5,
364		Speculative: true,
365	},
366	{
367		Provider: "openai-codex", ID: "gpt-5.6-luna", DisplayName: "GPT-5.6 Luna",
368		ContextWindow: 372000, MaxOutput: 128000, Reasoning: true,
369		PriceInput: 1, PriceOutput: 6, PriceCacheRead: 0.1, PriceCacheWrite: 1.25,
370		Speculative: true,
371	},
372	{
373		Provider: "openai-codex", ID: "gpt-5.6-sol", DisplayName: "GPT-5.6 Sol",
374		ContextWindow: 372000, MaxOutput: 128000, Reasoning: true,
375		PriceInput: 5, PriceOutput: 30, PriceCacheRead: 0.5, PriceCacheWrite: 6.25,
376		Speculative: true,
377	},
378	{
379		Provider: "openai-codex", ID: "gpt-5.6-terra", DisplayName: "GPT-5.6 Terra",
380		ContextWindow: 372000, MaxOutput: 128000, Reasoning: true,
381		PriceInput: 2.5, PriceOutput: 15, PriceCacheRead: 0.25, PriceCacheWrite: 3.125,
382		Speculative: true,
383	},
384}
385
386// DefaultModel is used when the user does not specify one.
387var DefaultModel = Catalog[0] // claude-sonnet-4-5
388
389// ----- active (merged) catalog -----
390//
391// Callers should use Active() / FindModel / ModelsForProvider for
392// lookups. They return the baked-in Catalog merged with any live
393// models loaded via SetLiveModels.
394
395var (
396	activeMu  sync.RWMutex
397	active    []Model // live overlay merged in via SetLiveModels; nil = none yet
398	activeSet bool    // true once SetLiveModels has run (even with empty live)
399)
400
401// SetLiveModels replaces the "live" overlay used by the active catalog.
402// Typically called after a successful /v1/models discovery or on load
403// from the on-disk cache.
404func SetLiveModels(live []Model) {
405	activeMu.Lock()
406	defer activeMu.Unlock()
407	activeSet = true
408	if len(live) == 0 {
409		active = nil
410		return
411	}
412	active = MergeCatalog(live)
413}
414
415// Active returns the current merged catalog.
416//
417// When no live overlay has been set it returns the fully-assembled
418// static Catalog. Reading Catalog here (rather than capturing it into
419// a package-level var initializer) is load-bearing: the extended
420// catalog in catalog_builtin.go / extra_models.go is appended from
421// init() functions, which run AFTER package-level var initializers.
422// Snapshotting Catalog at var-init time would freeze the picker to the
423// curated seed list and drop every extra provider (openrouter, groq,
424// xai, ...). Deferring the read to call time avoids that ordering trap.
425func Active() []Model {
426	activeMu.RLock()
427	defer activeMu.RUnlock()
428	src := active
429	if !activeSet || src == nil {
430		src = Catalog
431	}
432	out := make([]Model, len(src))
433	copy(out, src)
434	return out
435}
436
437// FindModel returns a Model by id, optionally constrained by provider.
438// If provider is empty, the first matching id is returned. Looks up
439// against the merged active catalog.
440func FindModel(provider, id string) (Model, error) {
441	for _, m := range Active() {
442		if m.ID == id && (provider == "" || m.Provider == provider) {
443			return m, nil
444		}
445	}
446	return Model{}, fmt.Errorf("unknown model %q (provider=%q)", id, provider)
447}
448
449// ModelsForProvider returns all models for the given provider, from the
450// merged active catalog.
451func ModelsForProvider(provider string) []Model {
452	var out []Model
453	for _, m := range Active() {
454		if m.Provider == provider {
455			out = append(out, m)
456		}
457	}
458	return out
459}
460
461// ComputeCost returns the USD cost for the given usage on model m.
462func ComputeCost(m Model, u Usage) float64 {
463	const per = 1_000_000.0
464	return float64(u.InputTokens)*m.PriceInput/per +
465		float64(u.OutputTokens)*m.PriceOutput/per +
466		float64(u.CacheReadTokens)*m.PriceCacheRead/per +
467		float64(u.CacheWriteTokens)*m.PriceCacheWrite/per
468}

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 1

Tags 476

↑↓ moveenter openesc close