v0.33.1 9577fee

keybindings.go RawHistoryFolder

1package handlers
2
3import (
4	"context"
5	"encoding/json"
6	"fmt"
7
8	"github.com/bnema/dumber/internal/application/port"
9	"github.com/bnema/dumber/internal/logging"
10)
11
12// KeybindingsHandler handles keybinding-related messages.
13type KeybindingsHandler struct {
14	getUC      port.KeybindingsGetter
15	setUC      port.KeybindingSetter
16	resetUC    port.KeybindingResetter
17	resetAllUC port.AllKeybindingsResetter
18}
19
20// NewKeybindingsHandler creates a new KeybindingsHandler.
21func NewKeybindingsHandler(
22	getUC port.KeybindingsGetter,
23	setUC port.KeybindingSetter,
24	resetUC port.KeybindingResetter,
25	resetAllUC port.AllKeybindingsResetter,
26) *KeybindingsHandler {
27	return &KeybindingsHandler{
28		getUC:      getUC,
29		setUC:      setUC,
30		resetUC:    resetUC,
31		resetAllUC: resetAllUC,
32	}
33}
34
35// HandleGetKeybindings returns all keybindings grouped by mode.
36func (h *KeybindingsHandler) HandleGetKeybindings(ctx context.Context, _ port.WebViewID, _ json.RawMessage) (any, error) {
37	return h.getUC.Execute(ctx)
38}
39
40// HandleSetKeybinding updates a single keybinding.
41func (h *KeybindingsHandler) HandleSetKeybinding(ctx context.Context, _ port.WebViewID, payload json.RawMessage) (any, error) {
42	log := logging.FromContext(ctx).With().Str("handler", "keybindings").Logger()
43	log.Debug().RawJSON("payload", payload).Msg("HandleSetKeybinding called")
44
45	var req port.SetKeybindingRequest
46	if err := json.Unmarshal(payload, &req); err != nil {
47		log.Error().Err(err).RawJSON("payload", payload).Msg("failed to unmarshal request")
48		return nil, fmt.Errorf("invalid request: %w", err)
49	}
50
51	resp, err := h.setUC.Execute(ctx, req)
52	if err != nil {
53		return nil, err
54	}
55
56	return map[string]any{
57		"status":    "success",
58		"conflicts": resp.Conflicts,
59	}, nil
60}
61
62// HandleResetKeybinding resets a keybinding to default.
63func (h *KeybindingsHandler) HandleResetKeybinding(ctx context.Context, _ port.WebViewID, payload json.RawMessage) (any, error) {
64	log := logging.FromContext(ctx).With().Str("handler", "keybindings").Logger()
65
66	var req port.ResetKeybindingRequest
67	if err := json.Unmarshal(payload, &req); err != nil {
68		return nil, fmt.Errorf("invalid request: %w", err)
69	}
70
71	log.Info().Str("mode", req.Mode).Str("action", req.Action).Msg("resetting keybinding to default")
72
73	if err := h.resetUC.Execute(ctx, req); err != nil {
74		return nil, err
75	}
76
77	return map[string]any{"status": "success"}, nil
78}
79
80// HandleResetAllKeybindings resets all keybindings to defaults.
81func (h *KeybindingsHandler) HandleResetAllKeybindings(ctx context.Context, _ port.WebViewID, _ json.RawMessage) (any, error) {
82	log := logging.FromContext(ctx).With().Str("handler", "keybindings").Logger()
83	log.Info().Msg("resetting all keybindings to defaults")
84
85	if err := h.resetAllUC.Execute(ctx); err != nil {
86		return nil, err
87	}
88
89	return map[string]any{"status": "success"}, nil
90}
91
92// RegisterKeybindingsHandlers registers keybindings handlers with the router.
93func RegisterKeybindingsHandlers(ctx context.Context, router port.WebUIHandlerRouter, handler *KeybindingsHandler) error {
94	log := logging.FromContext(ctx).With().Str("component", "handlers").Logger()
95
96	for msgType, fn := range map[string]port.WebUIMessageHandlerFunc{
97		port.MsgGetKeybindings:      handler.HandleGetKeybindings,
98		port.MsgSetKeybinding:       handler.HandleSetKeybinding,
99		port.MsgResetKeybinding:     handler.HandleResetKeybinding,
100		port.MsgResetAllKeybindings: handler.HandleResetAllKeybindings,
101	} {
102		if err := port.RegisterWebUIMessage(router, msgType, fn); err != nil {
103			return err
104		}
105	}
106
107	log.Info().Msg("registered keybindings handlers")
108	return nil
109}

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 8

Tags 42

↑↓ moveenter openesc close